diff options
286 files changed, 26558 insertions, 6895 deletions
diff --git a/Documentation/ABI/testing/sysfs-class-regulator b/Documentation/ABI/testing/sysfs-class-regulator index 79a4a75b2d2c..3731f6f29bcb 100644 --- a/Documentation/ABI/testing/sysfs-class-regulator +++ b/Documentation/ABI/testing/sysfs-class-regulator @@ -1,7 +1,7 @@ What: /sys/class/regulator/.../state Date: April 2008 KernelVersion: 2.6.26 -Contact: Liam Girdwood <lg@opensource.wolfsonmicro.com> +Contact: Liam Girdwood <lrg@slimlogic.co.uk> Description: Each regulator directory will contain a field called state. This holds the regulator output state. @@ -27,7 +27,7 @@ Description: What: /sys/class/regulator/.../type Date: April 2008 KernelVersion: 2.6.26 -Contact: Liam Girdwood <lg@opensource.wolfsonmicro.com> +Contact: Liam Girdwood <lrg@slimlogic.co.uk> Description: Each regulator directory will contain a field called type. This holds the regulator type. @@ -51,7 +51,7 @@ Description: What: /sys/class/regulator/.../microvolts Date: April 2008 KernelVersion: 2.6.26 -Contact: Liam Girdwood <lg@opensource.wolfsonmicro.com> +Contact: Liam Girdwood <lrg@slimlogic.co.uk> Description: Each regulator directory will contain a field called microvolts. This holds the regulator output voltage setting @@ -65,7 +65,7 @@ Description: What: /sys/class/regulator/.../microamps Date: April 2008 KernelVersion: 2.6.26 -Contact: Liam Girdwood <lg@opensource.wolfsonmicro.com> +Contact: Liam Girdwood <lrg@slimlogic.co.uk> Description: Each regulator directory will contain a field called microamps. This holds the regulator output current limit @@ -79,7 +79,7 @@ Description: What: /sys/class/regulator/.../opmode Date: April 2008 KernelVersion: 2.6.26 -Contact: Liam Girdwood <lg@opensource.wolfsonmicro.com> +Contact: Liam Girdwood <lrg@slimlogic.co.uk> Description: Each regulator directory will contain a field called opmode. This holds the regulator operating mode setting. @@ -102,7 +102,7 @@ Description: What: /sys/class/regulator/.../min_microvolts Date: April 2008 KernelVersion: 2.6.26 -Contact: Liam Girdwood <lg@opensource.wolfsonmicro.com> +Contact: Liam Girdwood <lrg@slimlogic.co.uk> Description: Each regulator directory will contain a field called min_microvolts. This holds the minimum safe working regulator @@ -116,7 +116,7 @@ Description: What: /sys/class/regulator/.../max_microvolts Date: April 2008 KernelVersion: 2.6.26 -Contact: Liam Girdwood <lg@opensource.wolfsonmicro.com> +Contact: Liam Girdwood <lrg@slimlogic.co.uk> Description: Each regulator directory will contain a field called max_microvolts. This holds the maximum safe working regulator @@ -130,7 +130,7 @@ Description: What: /sys/class/regulator/.../min_microamps Date: April 2008 KernelVersion: 2.6.26 -Contact: Liam Girdwood <lg@opensource.wolfsonmicro.com> +Contact: Liam Girdwood <lrg@slimlogic.co.uk> Description: Each regulator directory will contain a field called min_microamps. This holds the minimum safe working regulator @@ -145,7 +145,7 @@ Description: What: /sys/class/regulator/.../max_microamps Date: April 2008 KernelVersion: 2.6.26 -Contact: Liam Girdwood <lg@opensource.wolfsonmicro.com> +Contact: Liam Girdwood <lrg@slimlogic.co.uk> Description: Each regulator directory will contain a field called max_microamps. This holds the maximum safe working regulator @@ -157,10 +157,23 @@ Description: platform code. +What: /sys/class/regulator/.../name +Date: October 2008 +KernelVersion: 2.6.28 +Contact: Liam Girdwood <lrg@slimlogic.co.uk> +Description: + Each regulator directory will contain a field called + name. This holds a string identifying the regulator for + display purposes. + + NOTE: this will be empty if no suitable name is provided + by platform or regulator drivers. + + What: /sys/class/regulator/.../num_users Date: April 2008 KernelVersion: 2.6.26 -Contact: Liam Girdwood <lg@opensource.wolfsonmicro.com> +Contact: Liam Girdwood <lrg@slimlogic.co.uk> Description: Each regulator directory will contain a field called num_users. This holds the number of consumer devices that @@ -170,7 +183,7 @@ Description: What: /sys/class/regulator/.../requested_microamps Date: April 2008 KernelVersion: 2.6.26 -Contact: Liam Girdwood <lg@opensource.wolfsonmicro.com> +Contact: Liam Girdwood <lrg@slimlogic.co.uk> Description: Each regulator directory will contain a field called requested_microamps. This holds the total requested load @@ -181,7 +194,7 @@ Description: What: /sys/class/regulator/.../parent Date: April 2008 KernelVersion: 2.6.26 -Contact: Liam Girdwood <lg@opensource.wolfsonmicro.com> +Contact: Liam Girdwood <lrg@slimlogic.co.uk> Description: Some regulator directories will contain a link called parent. This points to the parent or supply regulator if one exists. @@ -189,7 +202,7 @@ Description: What: /sys/class/regulator/.../suspend_mem_microvolts Date: May 2008 KernelVersion: 2.6.26 -Contact: Liam Girdwood <lg@opensource.wolfsonmicro.com> +Contact: Liam Girdwood <lrg@slimlogic.co.uk> Description: Each regulator directory will contain a field called suspend_mem_microvolts. This holds the regulator output @@ -203,7 +216,7 @@ Description: What: /sys/class/regulator/.../suspend_disk_microvolts Date: May 2008 KernelVersion: 2.6.26 -Contact: Liam Girdwood <lg@opensource.wolfsonmicro.com> +Contact: Liam Girdwood <lrg@slimlogic.co.uk> Description: Each regulator directory will contain a field called suspend_disk_microvolts. This holds the regulator output @@ -217,7 +230,7 @@ Description: What: /sys/class/regulator/.../suspend_standby_microvolts Date: May 2008 KernelVersion: 2.6.26 -Contact: Liam Girdwood <lg@opensource.wolfsonmicro.com> +Contact: Liam Girdwood <lrg@slimlogic.co.uk> Description: Each regulator directory will contain a field called suspend_standby_microvolts. This holds the regulator output @@ -231,7 +244,7 @@ Description: What: /sys/class/regulator/.../suspend_mem_mode Date: May 2008 KernelVersion: 2.6.26 -Contact: Liam Girdwood <lg@opensource.wolfsonmicro.com> +Contact: Liam Girdwood <lrg@slimlogic.co.uk> Description: Each regulator directory will contain a field called suspend_mem_mode. This holds the regulator operating mode @@ -245,7 +258,7 @@ Description: What: /sys/class/regulator/.../suspend_disk_mode Date: May 2008 KernelVersion: 2.6.26 -Contact: Liam Girdwood <lg@opensource.wolfsonmicro.com> +Contact: Liam Girdwood <lrg@slimlogic.co.uk> Description: Each regulator directory will contain a field called suspend_disk_mode. This holds the regulator operating mode @@ -258,7 +271,7 @@ Description: What: /sys/class/regulator/.../suspend_standby_mode Date: May 2008 KernelVersion: 2.6.26 -Contact: Liam Girdwood <lg@opensource.wolfsonmicro.com> +Contact: Liam Girdwood <lrg@slimlogic.co.uk> Description: Each regulator directory will contain a field called suspend_standby_mode. This holds the regulator operating mode @@ -272,7 +285,7 @@ Description: What: /sys/class/regulator/.../suspend_mem_state Date: May 2008 KernelVersion: 2.6.26 -Contact: Liam Girdwood <lg@opensource.wolfsonmicro.com> +Contact: Liam Girdwood <lrg@slimlogic.co.uk> Description: Each regulator directory will contain a field called suspend_mem_state. This holds the regulator operating state @@ -287,7 +300,7 @@ Description: What: /sys/class/regulator/.../suspend_disk_state Date: May 2008 KernelVersion: 2.6.26 -Contact: Liam Girdwood <lg@opensource.wolfsonmicro.com> +Contact: Liam Girdwood <lrg@slimlogic.co.uk> Description: Each regulator directory will contain a field called suspend_disk_state. This holds the regulator operating state @@ -302,7 +315,7 @@ Description: What: /sys/class/regulator/.../suspend_standby_state Date: May 2008 KernelVersion: 2.6.26 -Contact: Liam Girdwood <lg@opensource.wolfsonmicro.com> +Contact: Liam Girdwood <lrg@slimlogic.co.uk> Description: Each regulator directory will contain a field called suspend_standby_state. This holds the regulator operating diff --git a/Documentation/feature-removal-schedule.txt b/Documentation/feature-removal-schedule.txt index cc8093c15cf5..4d2566a7d168 100644 --- a/Documentation/feature-removal-schedule.txt +++ b/Documentation/feature-removal-schedule.txt @@ -287,6 +287,13 @@ Who: Glauber Costa <gcosta@redhat.com> --------------------------- +What: remove HID compat support +When: 2.6.29 +Why: needed only as a temporary solution until distros fix themselves up +Who: Jiri Slaby <jirislaby@gmail.com> + +--------------------------- + What: /sys/o2cb symlink When: January 2010 Why: /sys/fs/o2cb is the proper location for this information - /sys/o2cb diff --git a/Documentation/filesystems/ocfs2.txt b/Documentation/filesystems/ocfs2.txt index c318a8bbb1ef..4340cc825796 100644 --- a/Documentation/filesystems/ocfs2.txt +++ b/Documentation/filesystems/ocfs2.txt @@ -76,3 +76,9 @@ localalloc=8(*) Allows custom localalloc size in MB. If the value is too large, the fs will silently revert it to the default. Localalloc is not enabled for local mounts. localflocks This disables cluster aware flock. +inode64 Indicates that Ocfs2 is allowed to create inodes at + any location in the filesystem, including those which + will result in inode numbers occupying more than 32 + bits of significance. +user_xattr (*) Enables Extended User Attributes. +nouser_xattr Disables Extended User Attributes. diff --git a/Documentation/power/regulator/machine.txt b/Documentation/power/regulator/machine.txt index c9a35665cf70..ce3487d99abe 100644 --- a/Documentation/power/regulator/machine.txt +++ b/Documentation/power/regulator/machine.txt @@ -2,17 +2,8 @@ Regulator Machine Driver Interface =================================== The regulator machine driver interface is intended for board/machine specific -initialisation code to configure the regulator subsystem. Typical things that -machine drivers would do are :- +initialisation code to configure the regulator subsystem. - 1. Regulator -> Device mapping. - 2. Regulator supply configuration. - 3. Power Domain constraint setting. - - - -1. Regulator -> device mapping -============================== Consider the following machine :- Regulator-1 -+-> Regulator-2 --> [Consumer A @ 1.8 - 2.0V] @@ -21,81 +12,82 @@ Consider the following machine :- The drivers for consumers A & B must be mapped to the correct regulator in order to control their power supply. This mapping can be achieved in machine -initialisation code by calling :- +initialisation code by creating a struct regulator_consumer_supply for +each regulator. + +struct regulator_consumer_supply { + struct device *dev; /* consumer */ + const char *supply; /* consumer supply - e.g. "vcc" */ +}; -int regulator_set_device_supply(const char *regulator, struct device *dev, - const char *supply); +e.g. for the machine above -and is shown with the following code :- +static struct regulator_consumer_supply regulator1_consumers[] = { +{ + .dev = &platform_consumerB_device.dev, + .supply = "Vcc", +},}; -regulator_set_device_supply("Regulator-1", devB, "Vcc"); -regulator_set_device_supply("Regulator-2", devA, "Vcc"); +static struct regulator_consumer_supply regulator2_consumers[] = { +{ + .dev = &platform_consumerA_device.dev, + .supply = "Vcc", +},}; This maps Regulator-1 to the 'Vcc' supply for Consumer B and maps Regulator-2 to the 'Vcc' supply for Consumer A. - -2. Regulator supply configuration. -================================== -Consider the following machine (again) :- - - Regulator-1 -+-> Regulator-2 --> [Consumer A @ 1.8 - 2.0V] - | - +-> [Consumer B @ 3.3V] +Constraints can now be registered by defining a struct regulator_init_data +for each regulator power domain. This structure also maps the consumers +to their supply regulator :- + +static struct regulator_init_data regulator1_data = { + .constraints = { + .min_uV = 3300000, + .max_uV = 3300000, + .valid_modes_mask = REGULATOR_MODE_NORMAL, + }, + .num_consumer_supplies = ARRAY_SIZE(regulator1_consumers), + .consumer_supplies = regulator1_consumers, +}; Regulator-1 supplies power to Regulator-2. This relationship must be registered with the core so that Regulator-1 is also enabled when Consumer A enables it's -supply (Regulator-2). - -This relationship can be register with the core via :- - -int regulator_set_supply(const char *regulator, const char *regulator_supply); - -In this example we would use the following code :- - -regulator_set_supply("Regulator-2", "Regulator-1"); - -Relationships can be queried by calling :- - -const char *regulator_get_supply(const char *regulator); - - -3. Power Domain constraint setting. -=================================== -Each power domain within a system has physical constraints on voltage and -current. This must be defined in software so that the power domain is always -operated within specifications. - -Consider the following machine (again) :- - - Regulator-1 -+-> Regulator-2 --> [Consumer A @ 1.8 - 2.0V] - | - +-> [Consumer B @ 3.3V] - -This gives us two regulators and two power domains: - - Domain 1: Regulator-2, Consumer B. - Domain 2: Consumer A. - -Constraints can be registered by calling :- - -int regulator_set_platform_constraints(const char *regulator, - struct regulation_constraints *constraints); - -The example is defined as follows :- - -struct regulation_constraints domain_1 = { - .min_uV = 3300000, - .max_uV = 3300000, - .valid_modes_mask = REGULATOR_MODE_NORMAL, +supply (Regulator-2). The supply regulator is set by the supply_regulator_dev +field below:- + +static struct regulator_init_data regulator2_data = { + .supply_regulator_dev = &platform_regulator1_device.dev, + .constraints = { + .min_uV = 1800000, + .max_uV = 2000000, + .valid_ops_mask = REGULATOR_CHANGE_VOLTAGE, + .valid_modes_mask = REGULATOR_MODE_NORMAL, + }, + .num_consumer_supplies = ARRAY_SIZE(regulator2_consumers), + .consumer_supplies = regulator2_consumers, }; -struct regulation_constraints domain_2 = { - .min_uV = 1800000, - .max_uV = 2000000, - .valid_ops_mask = REGULATOR_CHANGE_VOLTAGE, - .valid_modes_mask = REGULATOR_MODE_NORMAL, +Finally the regulator devices must be registered in the usual manner. + +static struct platform_device regulator_devices[] = { +{ + .name = "regulator", + .id = DCDC_1, + .dev = { + .platform_data = ®ulator1_data, + }, +}, +{ + .name = "regulator", + .id = DCDC_2, + .dev = { + .platform_data = ®ulator2_data, + }, +}, }; +/* register regulator 1 device */ +platform_device_register(&wm8350_regulator_devices[0]); -regulator_set_platform_constraints("Regulator-1", &domain_1); -regulator_set_platform_constraints("Regulator-2", &domain_2); +/* register regulator 2 device */ +platform_device_register(&wm8350_regulator_devices[1]); diff --git a/Documentation/power/regulator/regulator.txt b/Documentation/power/regulator/regulator.txt index a69050143592..4200accb9bba 100644 --- a/Documentation/power/regulator/regulator.txt +++ b/Documentation/power/regulator/regulator.txt @@ -10,11 +10,11 @@ Registration Drivers can register a regulator by calling :- -struct regulator_dev *regulator_register(struct regulator_desc *regulator_desc, - void *reg_data); +struct regulator_dev *regulator_register(struct device *dev, + struct regulator_desc *regulator_desc); -This will register the regulators capabilities and operations the regulator -core. The core does not touch reg_data (private to regulator driver). +This will register the regulators capabilities and operations to the regulator +core. Regulators can be unregistered by calling :- diff --git a/MAINTAINERS b/MAINTAINERS index 988b0a852890..5d0b8a23d639 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -4520,10 +4520,11 @@ S: Maintained VOLTAGE AND CURRENT REGULATOR FRAMEWORK P: Liam Girdwood -M: lg@opensource.wolfsonmicro.com +M: lrg@slimlogic.co.uk P: Mark Brown M: broonie@opensource.wolfsonmicro.com W: http://opensource.wolfsonmicro.com/node/15 +W: http://www.slimlogic.co.uk/?page_id=5 T: git kernel.org/pub/scm/linux/kernel/git/lrg/voltage-2.6.git S: Supported diff --git a/arch/m68k/Kconfig b/arch/m68k/Kconfig index 41e5bf02e230..677c93a490f6 100644 --- a/arch/m68k/Kconfig +++ b/arch/m68k/Kconfig @@ -105,21 +105,9 @@ config PCMCIA To compile this driver as modules, choose M here: the modules will be called pcmcia_core and ds. -config SUN3 - bool "Sun3 support" - select M68020 - select MMU_SUN3 if MMU - help - This option enables support for the Sun 3 series of workstations - (3/50, 3/60, 3/1xx, 3/2xx systems). Enabling this option requires - that all other hardware types must be disabled, as Sun 3 kernels - are incompatible with all other m68k targets (including Sun 3x!). - - If you don't want to compile a kernel exclusively for a Sun 3, say N. - config AMIGA bool "Amiga support" - depends on !MMU_SUN3 + select MMU_MOTOROLA if MMU help This option enables support for the Amiga series of computers. If you plan to use this kernel on an Amiga, say Y here and browse the @@ -127,33 +115,16 @@ config AMIGA config ATARI bool "Atari support" - depends on !MMU_SUN3 + select MMU_MOTOROLA if MMU help This option enables support for the 68000-based Atari series of computers (including the TT, Falcon and Medusa). If you plan to use this kernel on an Atari, say Y here and browse the material available in <file:Documentation/m68k>; otherwise say N. -config HADES - bool "Hades support" - depends on ATARI && BROKEN - help - This option enables support for the Hades Atari clone. If you plan - to use this kernel on a Hades, say Y here; otherwise say N. - -config PCI - bool - depends on HADES - default y - help - Find out whether you have a PCI motherboard. PCI is the name of a - bus system, i.e. the way the CPU talks to the other stuff inside - your box. Other bus systems are ISA, EISA, MicroChannel (MCA) or - VESA. If you have PCI, say Y, otherwise N. - config MAC bool "Macintosh support" - depends on !MMU_SUN3 + select MMU_MOTOROLA if MMU help This option enables support for the Apple Macintosh series of computers (yes, there is experimental support now, at least for part @@ -174,14 +145,14 @@ config M68K_L2_CACHE config APOLLO bool "Apollo support" - depends on !MMU_SUN3 + select MMU_MOTOROLA if MMU help Say Y here if you want to run Linux on an MC680x0-based Apollo Domain workstation such as the DN3500. config VME bool "VME (Motorola and BVM) support" - depends on !MMU_SUN3 + select MMU_MOTOROLA if MMU help Say Y here if you want to build a kernel for a 680x0 based VME board. Boards currently supported include Motorola boards MVME147, @@ -218,7 +189,7 @@ config BVME6000 config HP300 bool "HP9000/300 and HP9000/400 support" - depends on !MMU_SUN3 + select MMU_MOTOROLA if MMU help This option enables support for the HP9000/300 and HP9000/400 series of workstations. Support for these machines is still somewhat @@ -237,7 +208,7 @@ config DIO config SUN3X bool "Sun3x support" - depends on !MMU_SUN3 + select MMU_MOTOROLA if MMU select M68030 help This option enables support for the Sun 3x series of workstations. @@ -250,7 +221,7 @@ config SUN3X config Q40 bool "Q40/Q60 support" - depends on !MMU_SUN3 + select MMU_MOTOROLA if MMU help The Q40 is a Motorola 68040-based successor to the Sinclair QL manufactured in Germany. There is an official Q40 home page at @@ -258,6 +229,19 @@ config Q40 Q60. Select your CPU below. For 68LC060 don't forget to enable FPU emulation. +config SUN3 + bool "Sun3 support" + depends on !MMU_MOTOROLA + select MMU_SUN3 if MMU + select M68020 + help + This option enables support for the Sun 3 series of workstations + (3/50, 3/60, 3/1xx, 3/2xx systems). Enabling this option requires + that all other hardware types must be disabled, as Sun 3 kernels + are incompatible with all other m68k targets (including Sun 3x!). + + If you don't want to compile a kernel exclusively for a Sun 3, say N. + comment "Processor type" config M68020 @@ -295,10 +279,10 @@ config M68060 config MMU_MOTOROLA bool depends on MMU && !MMU_SUN3 - default y config MMU_SUN3 bool + depends on MMU && !MMU_MOTOROLA config M68KFPU_EMU bool "Math emulation support (EXPERIMENTAL)" diff --git a/arch/m68k/amiga/config.c b/arch/m68k/amiga/config.c index df679d96b1cb..0a3f9e8ebde0 100644 --- a/arch/m68k/amiga/config.c +++ b/arch/m68k/amiga/config.c @@ -24,6 +24,7 @@ #include <linux/interrupt.h> #include <linux/zorro.h> #include <linux/module.h> +#include <linux/keyboard.h> #include <asm/bootinfo.h> #include <asm/setup.h> @@ -984,3 +985,11 @@ static int amiga_get_hardware_list(char *buffer) return len; } + +/* + * The Amiga keyboard driver needs key_maps, but we cannot export it in + * drivers/char/defkeymap.c, as it is autogenerated + */ +#ifdef CONFIG_HW_CONSOLE +EXPORT_SYMBOL_GPL(key_maps); +#endif diff --git a/arch/m68k/atari/Makefile b/arch/m68k/atari/Makefile index 2cd905efe63a..0cac723306f9 100644 --- a/arch/m68k/atari/Makefile +++ b/arch/m68k/atari/Makefile @@ -5,7 +5,4 @@ obj-y := config.o time.o debug.o ataints.o stdma.o \ atasound.o stram.o -ifeq ($(CONFIG_PCI),y) -obj-$(CONFIG_HADES) += hades-pci.o -endif obj-$(CONFIG_ATARI_KBD_CORE) += atakeyb.o diff --git a/arch/m68k/atari/ataints.c b/arch/m68k/atari/ataints.c index b45593a60bdd..dba4afabb444 100644 --- a/arch/m68k/atari/ataints.c +++ b/arch/m68k/atari/ataints.c @@ -407,10 +407,8 @@ void __init atari_init_IRQ(void) * gets overruns) */ - if (!MACH_IS_HADES) { - vectors[VEC_INT2] = falcon_hblhandler; - vectors[VEC_INT4] = falcon_hblhandler; - } + vectors[VEC_INT2] = falcon_hblhandler; + vectors[VEC_INT4] = falcon_hblhandler; } if (ATARIHW_PRESENT(PCM_8BIT) && ATARIHW_PRESENT(MICROWIRE)) { diff --git a/arch/m68k/atari/atakeyb.c b/arch/m68k/atari/atakeyb.c index bb959fbab2dc..c038b7c7eff0 100644 --- a/arch/m68k/atari/atakeyb.c +++ b/arch/m68k/atari/atakeyb.c @@ -635,15 +635,3 @@ int atari_keyb_init(void) return 0; } EXPORT_SYMBOL_GPL(atari_keyb_init); - -int atari_kbd_translate(unsigned char keycode, unsigned char *keycodep, char raw_mode) -{ -#ifdef CONFIG_MAGIC_SYSRQ - /* ALT+HELP pressed? */ - if ((keycode == 98) && ((shift_state & 0xff) == 8)) - *keycodep = 0xff; - else -#endif - *keycodep = keycode; - return 1; -} diff --git a/arch/m68k/atari/config.c b/arch/m68k/atari/config.c index 5945e1505558..af031855f796 100644 --- a/arch/m68k/atari/config.c +++ b/arch/m68k/atari/config.c @@ -231,7 +231,7 @@ void __init config_atari(void) */ printk("Atari hardware found: "); - if (MACH_IS_MEDUSA || MACH_IS_HADES) { + if (MACH_IS_MEDUSA) { /* There's no Atari video hardware on the Medusa, but all the * addresses below generate a DTACK so no bus error occurs! */ } else if (hwreg_present(f030_xreg)) { @@ -269,10 +269,6 @@ void __init config_atari(void) ATARIHW_SET(SCSI_DMA); printk("TT_SCSI_DMA "); } - if (!MACH_IS_HADES && hwreg_present(&st_dma.dma_hi)) { - ATARIHW_SET(STND_DMA); - printk("STND_DMA "); - } /* * The ST-DMA address registers aren't readable * on all Medusas, so the test below may fail @@ -294,12 +290,11 @@ void __init config_atari(void) ATARIHW_SET(YM_2149); printk("YM2149 "); } - if (!MACH_IS_MEDUSA && !MACH_IS_HADES && - hwreg_present(&tt_dmasnd.ctrl)) { + if (!MACH_IS_MEDUSA && hwreg_present(&tt_dmasnd.ctrl)) { ATARIHW_SET(PCM_8BIT); printk("PCM "); } - if (!MACH_IS_HADES && hwreg_present(&falcon_codec.unused5)) { + if (hwreg_present(&falcon_codec.unused5)) { ATARIHW_SET(CODEC); printk("CODEC "); } @@ -313,7 +308,7 @@ void __init config_atari(void) (tt_scc_dma.dma_ctrl = 0x01, (tt_scc_dma.dma_ctrl & 1) == 1) && (tt_scc_dma.dma_ctrl = 0x00, (tt_scc_dma.dma_ctrl & 1) == 0) #else - !MACH_IS_MEDUSA && !MACH_IS_HADES + !MACH_IS_MEDUSA #endif ) { ATARIHW_SET(SCC_DMA); @@ -327,10 +322,7 @@ void __init config_atari(void) ATARIHW_SET(ST_ESCC); printk("ST_ESCC "); } - if (MACH_IS_HADES) { - ATARIHW_SET(VME); - printk("VME "); - } else if (hwreg_present(&tt_scu.sys_mask)) { + if (hwreg_present(&tt_scu.sys_mask)) { ATARIHW_SET(SCU); /* Assume a VME bus if there's a SCU */ ATARIHW_SET(VME); @@ -340,7 +332,7 @@ void __init config_atari(void) ATARIHW_SET(ANALOG_JOY); printk("ANALOG_JOY "); } - if (!MACH_IS_HADES && hwreg_present(blitter.halftone)) { + if (hwreg_present(blitter.halftone)) { ATARIHW_SET(BLITTER); printk("BLITTER "); } @@ -349,8 +341,7 @@ void __init config_atari(void) printk("IDE "); } #if 1 /* This maybe wrong */ - if (!MACH_IS_MEDUSA && !MACH_IS_HADES && - hwreg_present(&tt_microwire.data) && + if (!MACH_IS_MEDUSA && hwreg_present(&tt_microwire.data) && hwreg_present(&tt_microwire.mask) && (tt_microwire.mask = 0x7ff, udelay(1), @@ -369,19 +360,18 @@ void __init config_atari(void) mach_hwclk = atari_tt_hwclk; mach_set_clock_mmss = atari_tt_set_clock_mmss; } - if (!MACH_IS_HADES && hwreg_present(&mste_rtc.sec_ones)) { + if (hwreg_present(&mste_rtc.sec_ones)) { ATARIHW_SET(MSTE_CLK); printk("MSTE_CLK "); mach_hwclk = atari_mste_hwclk; mach_set_clock_mmss = atari_mste_set_clock_mmss; } - if (!MACH_IS_MEDUSA && !MACH_IS_HADES && - hwreg_present(&dma_wd.fdc_speed) && + if (!MACH_IS_MEDUSA && hwreg_present(&dma_wd.fdc_speed) && hwreg_write(&dma_wd.fdc_speed, 0)) { ATARIHW_SET(FDCSPEED); printk("FDC_SPEED "); } - if (!MACH_IS_HADES && !ATARIHW_PRESENT(ST_SCSI)) { + if (!ATARIHW_PRESENT(ST_SCSI)) { ATARIHW_SET(ACSI); printk("ACSI "); } @@ -449,7 +439,7 @@ void __init config_atari(void) * 0xFFxxxxxx -> 0x00xxxxxx, so that the first 16MB is accessible * in the last 16MB of the address space. */ - tos_version = (MACH_IS_MEDUSA || MACH_IS_HADES) ? + tos_version = (MACH_IS_MEDUSA) ? 0xfff : *(unsigned short *)0xff000002; atari_rtc_year_offset = (tos_version < 0x306) ? 70 : 68; } @@ -511,8 +501,7 @@ static void atari_reset(void) * On the Medusa, phys. 0x4 may contain garbage because it's no * ROM. See above for explanation why we cannot use PTOV(4). */ - reset_addr = MACH_IS_HADES ? 0x7fe00030 : - MACH_IS_MEDUSA || MACH_IS_AB40 ? 0xe00030 : + reset_addr = MACH_IS_MEDUSA || MACH_IS_AB40 ? 0xe00030 : *(unsigned long *) 0xff000004; /* reset ACIA for switch off OverScan, if it's active */ @@ -606,8 +595,6 @@ static void atari_get_model(char *model) if (MACH_IS_MEDUSA) /* Medusa has TT _MCH cookie */ strcat(model, "Medusa"); - else if (MACH_IS_HADES) - strcat(model, "Hades"); else strcat(model, "TT"); break; diff --git a/arch/m68k/atari/hades-pci.c b/arch/m68k/atari/hades-pci.c deleted file mode 100644 index 2bbabc008708..000000000000 --- a/arch/m68k/atari/hades-pci.c +++ /dev/null @@ -1,440 +0,0 @@ -/* - * hades-pci.c - Hardware specific PCI BIOS functions the Hades Atari clone. - * - * Written by Wout Klaren. - */ - -#include <linux/init.h> -#include <linux/kernel.h> -#include <asm/io.h> - -#if 0 -# define DBG_DEVS(args) printk args -#else -# define DBG_DEVS(args) -#endif - -#if defined(CONFIG_PCI) && defined(CONFIG_HADES) - -#include <linux/slab.h> -#include <linux/mm.h> -#include <linux/pci.h> - -#include <asm/atarihw.h> -#include <asm/atariints.h> -#include <asm/byteorder.h> -#include <asm/pci.h> - -#define HADES_MEM_BASE 0x80000000 -#define HADES_MEM_SIZE 0x20000000 -#define HADES_CONFIG_BASE 0xA0000000 -#define HADES_CONFIG_SIZE 0x10000000 -#define HADES_IO_BASE 0xB0000000 -#define HADES_IO_SIZE 0x10000000 -#define HADES_VIRT_IO_SIZE 0x00010000 /* Only 64k is remapped and actually used. */ - -#define N_SLOTS 4 /* Number of PCI slots. */ - -static const char pci_mem_name[] = "PCI memory space"; -static const char pci_io_name[] = "PCI I/O space"; -static const char pci_config_name[] = "PCI config space"; - -static struct resource config_space = { - .name = pci_config_name, - .start = HADES_CONFIG_BASE, - .end = HADES_CONFIG_BASE + HADES_CONFIG_SIZE - 1 -}; -static struct resource io_space = { - .name = pci_io_name, - .start = HADES_IO_BASE, - .end = HADES_IO_BASE + HADES_IO_SIZE - 1 -}; - -static const unsigned long pci_conf_base_phys[] = { - 0xA0080000, 0xA0040000, 0xA0020000, 0xA0010000 -}; -static unsigned long pci_conf_base_virt[N_SLOTS]; -static unsigned long pci_io_base_virt; - -/* - * static void *mk_conf_addr(unsigned char bus, unsigned char device_fn, - * unsigned char where) - * - * Calculate the address of the PCI configuration area of the given - * device. - * - * BUG: boards with multiple functions are probably not correctly - * supported. - */ - -static void *mk_conf_addr(struct pci_dev *dev, int where) -{ - int device = dev->devfn >> 3, function = dev->devfn & 7; - void *result; - - DBG_DEVS(("mk_conf_addr(bus=%d ,device_fn=0x%x, where=0x%x, pci_addr=0x%p)\n", - dev->bus->number, dev->devfn, where, pci_addr)); - - if (device > 3) - { - DBG_DEVS(("mk_conf_addr: device (%d) > 3, returning NULL\n", device)); - return NULL; - } - - if (dev->bus->number != 0) - { - DBG_DEVS(("mk_conf_addr: bus (%d) > 0, returning NULL\n", device)); - return NULL; - } - - result = (void *) (pci_conf_base_virt[device] | (function << 8) | (where)); - DBG_DEVS(("mk_conf_addr: returning pci_addr 0x%lx\n", (unsigned long) result)); - return result; -} - -static int hades_read_config_byte(struct pci_dev *dev, int where, u8 *value) -{ - volatile unsigned char *pci_addr; - - *value = 0xff; - - if ((pci_addr = (unsigned char *) mk_conf_addr(dev, where)) == NULL) - return PCIBIOS_DEVICE_NOT_FOUND; - - *value = *pci_addr; - - return PCIBIOS_SUCCESSFUL; -} - -static int hades_read_config_word(struct pci_dev *dev, int where, u16 *value) -{ - volatile unsigned short *pci_addr; - - *value = 0xffff; - - if (where & 0x1) - return PCIBIOS_BAD_REGISTER_NUMBER; - - if ((pci_addr = (unsigned short *) mk_conf_addr(dev, where)) == NULL) - return PCIBIOS_DEVICE_NOT_FOUND; - - *value = le16_to_cpu(*pci_addr); - - return PCIBIOS_SUCCESSFUL; -} - -static int hades_read_config_dword(struct pci_dev *dev, int where, u32 *value) -{ - volatile unsigned int *pci_addr; - unsigned char header_type; - int result; - - *value = 0xffffffff; - - if (where & 0x3) - return PCIBIOS_BAD_REGISTER_NUMBER; - - if ((pci_addr = (unsigned int *) mk_conf_addr(dev, where)) == NULL) - return PCIBIOS_DEVICE_NOT_FOUND; - - *value = le32_to_cpu(*pci_addr); - - /* - * Check if the value is an address on the bus. If true, add the - * base address of the PCI memory or PCI I/O area on the Hades. - */ - - if ((result = hades_read_config_byte(dev, PCI_HEADER_TYPE, - &header_type)) != PCIBIOS_SUCCESSFUL) - return result; - - if (((where >= PCI_BASE_ADDRESS_0) && (where <= PCI_BASE_ADDRESS_1)) || - ((header_type != PCI_HEADER_TYPE_BRIDGE) && ((where >= PCI_BASE_ADDRESS_2) && - (where <= PCI_BASE_ADDRESS_5)))) - { - if ((*value & PCI_BASE_ADDRESS_SPACE) == PCI_BASE_ADDRESS_SPACE_IO) - { - /* - * Base address register that contains an I/O address. If the - * address is valid on the Hades (0 <= *value < HADES_VIRT_IO_SIZE), - * add 'pci_io_base_virt' to the value. - */ - - if (*value < HADES_VIRT_IO_SIZE) - *value += pci_io_base_virt; - } - else - { - /* - * Base address register that contains an memory address. If the - * address is valid on the Hades (0 <= *value < HADES_MEM_SIZE), - * add HADES_MEM_BASE to the value. - */ - - if (*value == 0) - { - /* - * Base address is 0. Test if this base - * address register is used. - */ - - *pci_addr = 0xffffffff; - if (*pci_addr != 0) - { - *pci_addr = *value; - if (*value < HADES_MEM_SIZE) - *value += HADES_MEM_BASE; - } - } - else - { - if (*value < HADES_MEM_SIZE) - *value += HADES_MEM_BASE; - } - } - } - - return PCIBIOS_SUCCESSFUL; -} - -static int hades_write_config_byte(struct pci_dev *dev, int where, u8 value) -{ - volatile unsigned char *pci_addr; - - if ((pci_addr = (unsigned char *) mk_conf_addr(dev, where)) == NULL) - return PCIBIOS_DEVICE_NOT_FOUND; - - *pci_addr = value; - - return PCIBIOS_SUCCESSFUL; -} - -static int hades_write_config_word(struct pci_dev *dev, int where, u16 value) -{ - volatile unsigned short *pci_addr; - - if ((pci_addr = (unsigned short *) mk_conf_addr(dev, where)) == NULL) - return PCIBIOS_DEVICE_NOT_FOUND; - - *pci_addr = cpu_to_le16(value); - - return PCIBIOS_SUCCESSFUL; -} - -static int hades_write_config_dword(struct pci_dev *dev, int where, u32 value) -{ - volatile unsigned int *pci_addr; - unsigned char header_type; - int result; - - if ((pci_addr = (unsigned int *) mk_conf_addr(dev, where)) == NULL) - return PCIBIOS_DEVICE_NOT_FOUND; - - /* - * Check if the value is an address on the bus. If true, subtract the - * base address of the PCI memory or PCI I/O area on the Hades. - */ - - if ((result = hades_read_config_byte(dev, PCI_HEADER_TYPE, - &header_type)) != PCIBIOS_SUCCESSFUL) - return result; - - if (((where >= PCI_BASE_ADDRESS_0) && (where <= PCI_BASE_ADDRESS_1)) || - ((header_type != PCI_HEADER_TYPE_BRIDGE) && ((where >= PCI_BASE_ADDRESS_2) && - (where <= PCI_BASE_ADDRESS_5)))) - { - if ((value & PCI_BASE_ADDRESS_SPACE) == - PCI_BASE_ADDRESS_SPACE_IO) - { - /* - * I/O address. Check if the address is valid address on - * the Hades (pci_io_base_virt <= value < pci_io_base_virt + - * HADES_VIRT_IO_SIZE) or if the value is 0xffffffff. If not - * true do not write the base address register. If it is a - * valid base address subtract 'pci_io_base_virt' from the value. - */ - - if ((value >= pci_io_base_virt) && (value < (pci_io_base_virt + - HADES_VIRT_IO_SIZE))) - value -= pci_io_base_virt; - else - { - if (value != 0xffffffff) - return PCIBIOS_SET_FAILED; - } - } - else - { - /* - * Memory address. Check if the address is valid address on - * the Hades (HADES_MEM_BASE <= value < HADES_MEM_BASE + HADES_MEM_SIZE) or - * if the value is 0xffffffff. If not true do not write - * the base address register. If it is a valid base address - * subtract HADES_MEM_BASE from the value. - */ - - if ((value >= HADES_MEM_BASE) && (value < (HADES_MEM_BASE + HADES_MEM_SIZE))) - value -= HADES_MEM_BASE; - else - { - if (value != 0xffffffff) - return PCIBIOS_SET_FAILED; - } - } - } - - *pci_addr = cpu_to_le32(value); - - return PCIBIOS_SUCCESSFUL; -} - -/* - * static inline void hades_fixup(void) - * - * Assign IRQ numbers as used by Linux to the interrupt pins - * of the PCI cards. - */ - -static void __init hades_fixup(int pci_modify) -{ - char irq_tab[4] = { - [0] = IRQ_TT_MFP_IO0, /* Slot 0. */ - [1] = IRQ_TT_MFP_IO1, /* Slot 1. */ - [2] = IRQ_TT_MFP_SCC, /* Slot 2. */ - [3] = IRQ_TT_MFP_SCSIDMA /* Slot 3. */ - }; - struct pci_dev *dev = NULL; - unsigned char slot; - - /* - * Go through all devices, fixing up irqs as we see fit: - */ - - while ((dev = pci_get_device(PCI_ANY_ID, PCI_ANY_ID, dev)) != NULL) - { - if (dev->class >> 16 != PCI_BASE_CLASS_BRIDGE) - { - slot = PCI_SLOT(dev->devfn); /* Determine slot number. */ - dev->irq = irq_tab[slot]; - if (pci_modify) - pci_write_config_byte(dev, PCI_INTERRUPT_LINE, dev->irq); - } - } -} - -/* - * static void hades_conf_device(struct pci_dev *dev) - * - * Machine dependent Configure the given device. - * - * Parameters: - * - * dev - the pci device. - */ - -static void __init hades_conf_device(struct pci_dev *dev) -{ - pci_write_config_byte(dev, PCI_CACHE_LINE_SIZE, 0); -} - -static struct pci_ops hades_pci_ops = { - .read_byte = hades_read_config_byte, - .read_word = hades_read_config_word, - .read_dword = hades_read_config_dword, - .write_byte = hades_write_config_byte, - .write_word = hades_write_config_word, - .write_dword = hades_write_config_dword -}; - -/* - * struct pci_bus_info *init_hades_pci(void) - * - * Machine specific initialisation: - * - * - Allocate and initialise a 'pci_bus_info' structure - * - Initialise hardware - * - * Result: pointer to 'pci_bus_info' structure. - */ - -struct pci_bus_info * __init init_hades_pci(void) -{ - struct pci_bus_info *bus; - int i; - - /* - * Remap I/O and configuration space. - */ - - pci_io_base_virt = (unsigned long) ioremap(HADES_IO_BASE, HADES_VIRT_IO_SIZE); - - for (i = 0; i < N_SLOTS; i++) - pci_conf_base_virt[i] = (unsigned long) ioremap(pci_conf_base_phys[i], 0x10000); - - /* - * Allocate memory for bus info structure. - */ - - bus = kzalloc(sizeof(struct pci_bus_info), GFP_KERNEL); - if (unlikely(!bus)) - goto iounmap_base_virt; - - /* - * Claim resources. The m68k has no separate I/O space, both - * PCI memory space and PCI I/O space are in memory space. Therefore - * the I/O resources are requested in memory space as well. - */ - - if (unlikely(request_resource(&iomem_resource, &config_space) != 0)) - goto free_bus; - - if (unlikely(request_resource(&iomem_resource, &io_space) != 0)) - goto release_config_space; - - bus->mem_space.start = HADES_MEM_BASE; - bus->mem_space.end = HADES_MEM_BASE + HADES_MEM_SIZE - 1; - bus->mem_space.name = pci_mem_name; -#if 1 - if (unlikely(request_resource(&iomem_resource, &bus->mem_space) != 0)) - goto release_io_space; -#endif - bus->io_space.start = pci_io_base_virt; - bus->io_space.end = pci_io_base_virt + HADES_VIRT_IO_SIZE - 1; - bus->io_space.name = pci_io_name; -#if 1 - if (unlikely(request_resource(&ioport_resource, &bus->io_space) != 0)) - goto release_bus_mem_space; -#endif - /* - * Set hardware dependent functions. - */ - - bus->m68k_pci_ops = &hades_pci_ops; - bus->fixup = hades_fixup; - bus->conf_device = hades_conf_device; - - /* - * Select high to low edge for PCI interrupts. - */ - - tt_mfp.active_edge &= ~0x27; - - return bus; - -release_bus_mem_space: - release_resource(&bus->mem_space); -release_io_space: - release_resource(&io_space); -release_config_space: - release_resource(&config_space); -free_bus: - kfree(bus); -iounmap_base_virt: - iounmap((void *)pci_io_base_virt); - - for (i = 0; i < N_SLOTS; i++) - iounmap((void *)pci_conf_base_virt[i]); - - return NULL; -} -#endif diff --git a/arch/m68k/atari/time.c b/arch/m68k/atari/time.c index e0d3c8bfb408..1edde27fa32d 100644 --- a/arch/m68k/atari/time.c +++ b/arch/m68k/atari/time.c @@ -20,6 +20,9 @@ #include <asm/atariints.h> +DEFINE_SPINLOCK(rtc_lock); +EXPORT_SYMBOL_GPL(rtc_lock); + void __init atari_sched_init(irq_handler_t timer_routine) { @@ -191,13 +194,14 @@ int atari_tt_hwclk( int op, struct rtc_time *t ) } if (!(ctrl & RTC_DM_BINARY)) { - BIN_TO_BCD(sec); - BIN_TO_BCD(min); - BIN_TO_BCD(hour); - BIN_TO_BCD(day); - BIN_TO_BCD(mon); - BIN_TO_BCD(year); - if (wday >= 0) BIN_TO_BCD(wday); + sec = bin2bcd(sec); + min = bin2bcd(min); + hour = bin2bcd(hour); + day = bin2bcd(day); + mon = bin2bcd(mon); + year = bin2bcd(year); + if (wday >= 0) + wday = bin2bcd(wday); } } @@ -252,13 +256,13 @@ int atari_tt_hwclk( int op, struct rtc_time *t ) } if (!(ctrl & RTC_DM_BINARY)) { - BCD_TO_BIN(sec); - BCD_TO_BIN(min); - BCD_TO_BIN(hour); - BCD_TO_BIN(day); - BCD_TO_BIN(mon); - BCD_TO_BIN(year); - BCD_TO_BIN(wday); + sec = bcd2bin(sec); + min = bcd2bin(min); + hour = bcd2bin(hour); + day = bcd2bin(day); + mon = bcd2bin(mon); + year = bcd2bin(year); + wday = bcd2bin(wday); } if (!(ctrl & RTC_24H)) { @@ -318,7 +322,7 @@ int atari_tt_set_clock_mmss (unsigned long nowtime) rtc_minutes = RTC_READ (RTC_MINUTES); if (!(save_control & RTC_DM_BINARY)) - BCD_TO_BIN (rtc_minutes); + rtc_minutes = bcd2bin(rtc_minutes); /* Since we're only adjusting minutes and seconds, don't interfere with hour overflow. This avoids messing with unknown time zones @@ -329,8 +333,8 @@ int atari_tt_set_clock_mmss (unsigned long nowtime) { if (!(save_control & RTC_DM_BINARY)) { - BIN_TO_BCD (real_seconds); - BIN_TO_BCD (real_minutes); + real_seconds = bin2bcd(real_seconds); + real_minutes = bin2bcd(real_minutes); } RTC_WRITE (RTC_SECONDS, real_seconds); RTC_WRITE (RTC_MINUTES, real_minutes); diff --git a/arch/m68k/bvme6000/config.c b/arch/m68k/bvme6000/config.c index 9433a88a33c4..65c9204ab9ac 100644 --- a/arch/m68k/bvme6000/config.c +++ b/arch/m68k/bvme6000/config.c @@ -25,6 +25,7 @@ #include <linux/genhd.h> #include <linux/rtc.h> #include <linux/interrupt.h> +#include <linux/bcd.h> #include <asm/bootinfo.h> #include <asm/system.h> @@ -46,9 +47,6 @@ extern void bvme6000_reset (void); extern void bvme6000_waitbut(void); void bvme6000_set_vectors (void); -static unsigned char bcd2bin (unsigned char b); -static unsigned char bin2bcd (unsigned char b); - /* Save tick handler routine pointer, will point to do_timer() in * kernel/sched.c, called via bvme6000_process_int() */ @@ -264,17 +262,6 @@ unsigned long bvme6000_gettimeoffset (void) return v; } -static unsigned char bcd2bin (unsigned char b) -{ - return ((b>>4)*10 + (b&15)); -} - -static unsigned char bin2bcd (unsigned char b) -{ - return (((b/10)*16) + (b%10)); -} - - /* * Looks like op is non-zero for setting the clock, and zero for * reading the clock. diff --git a/arch/m68k/bvme6000/rtc.c b/arch/m68k/bvme6000/rtc.c index e8ac3f7d72df..808c9018b115 100644 --- a/arch/m68k/bvme6000/rtc.c +++ b/arch/m68k/bvme6000/rtc.c @@ -57,16 +57,16 @@ static int rtc_ioctl(struct inode *inode, struct file *file, unsigned int cmd, rtc->msr = 0x40; memset(&wtime, 0, sizeof(struct rtc_time)); do { - wtime.tm_sec = BCD2BIN(rtc->bcd_sec); - wtime.tm_min = BCD2BIN(rtc->bcd_min); - wtime.tm_hour = BCD2BIN(rtc->bcd_hr); - wtime.tm_mday = BCD2BIN(rtc->bcd_dom); - wtime.tm_mon = BCD2BIN(rtc->bcd_mth)-1; - wtime.tm_year = BCD2BIN(rtc->bcd_year); + wtime.tm_sec = bcd2bin(rtc->bcd_sec); + wtime.tm_min = bcd2bin(rtc->bcd_min); + wtime.tm_hour = bcd2bin(rtc->bcd_hr); + wtime.tm_mday = bcd2bin(rtc->bcd_dom); + wtime.tm_mon = bcd2bin(rtc->bcd_mth)-1; + wtime.tm_year = bcd2bin(rtc->bcd_year); if (wtime.tm_year < 70) wtime.tm_year += 100; - wtime.tm_wday = BCD2BIN(rtc->bcd_dow)-1; - } while (wtime.tm_sec != BCD2BIN(rtc->bcd_sec)); + wtime.tm_wday = bcd2bin(rtc->bcd_dow)-1; + } while (wtime.tm_sec != bcd2bin(rtc->bcd_sec)); rtc->msr = msr; local_irq_restore(flags); return copy_to_user(argp, &wtime, sizeof wtime) ? @@ -114,14 +114,14 @@ static int rtc_ioctl(struct inode *inode, struct file *file, unsigned int cmd, rtc->t0cr_rtmr = yrs%4; rtc->bcd_tenms = 0; - rtc->bcd_sec = BIN2BCD(sec); - rtc->bcd_min = BIN2BCD(min); - rtc->bcd_hr = BIN2BCD(hrs); - rtc->bcd_dom = BIN2BCD(day); - rtc->bcd_mth = BIN2BCD(mon); - rtc->bcd_year = BIN2BCD(yrs%100); + rtc->bcd_sec = bin2bcd(sec); + rtc->bcd_min = bin2bcd(min); + rtc->bcd_hr = bin2bcd(hrs); + rtc->bcd_dom = bin2bcd(day); + rtc->bcd_mth = bin2bcd(mon); + rtc->bcd_year = bin2bcd(yrs%100); if (rtc_tm.tm_wday >= 0) - rtc->bcd_dow = BIN2BCD(rtc_tm.tm_wday+1); + rtc->bcd_dow = bin2bcd(rtc_tm.tm_wday+1); rtc->t0cr_rtmr = yrs%4 | 0x08; rtc->msr = msr; diff --git a/arch/m68k/kernel/Makefile b/arch/m68k/kernel/Makefile index 3a7f62225504..55d5d6b680a2 100644 --- a/arch/m68k/kernel/Makefile +++ b/arch/m68k/kernel/Makefile @@ -14,5 +14,4 @@ obj-y := entry.o process.o traps.o ints.o signal.o ptrace.o module.o \ devres-y = ../../../kernel/irq/devres.o -obj-$(CONFIG_PCI) += bios32.o obj-y$(CONFIG_MMU_SUN3) += dma.o # no, it's not a typo diff --git a/arch/m68k/kernel/bios32.c b/arch/m68k/kernel/bios32.c deleted file mode 100644 index af170c2be735..000000000000 --- a/arch/m68k/kernel/bios32.c +++ /dev/null @@ -1,514 +0,0 @@ -/* - * bios32.c - PCI BIOS functions for m68k systems. - * - * Written by Wout Klaren. - * - * Based on the DEC Alpha bios32.c by Dave Rusling and David Mosberger. - */ - -#include <linux/init.h> -#include <linux/kernel.h> - -#if 0 -# define DBG_DEVS(args) printk args -#else -# define DBG_DEVS(args) -#endif - -#ifdef CONFIG_PCI - -/* - * PCI support for Linux/m68k. Currently only the Hades is supported. - * - * The support for PCI bridges in the DEC Alpha version has - * been removed in this version. - */ - -#include <linux/pci.h> -#include <linux/slab.h> -#include <linux/mm.h> - -#include <asm/io.h> -#include <asm/pci.h> -#include <asm/uaccess.h> - -#define KB 1024 -#define MB (1024*KB) -#define GB (1024*MB) - -#define MAJOR_REV 0 -#define MINOR_REV 5 - -/* - * Align VAL to ALIGN, which must be a power of two. - */ - -#define ALIGN(val,align) (((val) + ((align) - 1)) & ~((align) - 1)) - -/* - * Offsets relative to the I/O and memory base addresses from where resources - * are allocated. - */ - -#define IO_ALLOC_OFFSET 0x00004000 -#define MEM_ALLOC_OFFSET 0x04000000 - -/* - * Declarations of hardware specific initialisation functions. - */ - -extern struct pci_bus_info *init_hades_pci(void); - -/* - * Bus info structure of the PCI bus. A pointer to this structure is - * put in the sysdata member of the pci_bus structure. - */ - -static struct pci_bus_info *bus_info; - -static int pci_modify = 1; /* If set, layout the PCI bus ourself. */ -static int skip_vga; /* If set do not modify base addresses - of vga cards.*/ -static int disable_pci_burst; /* If set do not allow PCI bursts. */ - -static unsigned int io_base; -static unsigned int mem_base; - -/* - * static void disable_dev(struct pci_dev *dev) - * - * Disable PCI device DEV so that it does not respond to I/O or memory - * accesses. - * - * Parameters: - * - * dev - device to disable. - */ - -static void __init disable_dev(struct pci_dev *dev) -{ - unsigned short cmd; - - if (((dev->class >> 8 == PCI_CLASS_NOT_DEFINED_VGA) || - (dev->class >> 8 == PCI_CLASS_DISPLAY_VGA) || - (dev->class >> 8 == PCI_CLASS_DISPLAY_XGA)) && skip_vga) - return; - - pci_read_config_word(dev, PCI_COMMAND, &cmd); - - cmd &= (~PCI_COMMAND_IO & ~PCI_COMMAND_MEMORY & ~PCI_COMMAND_MASTER); - pci_write_config_word(dev, PCI_COMMAND, cmd); -} - -/* - * static void layout_dev(struct pci_dev *dev) - * - * Layout memory and I/O for a device. - * - * Parameters: - * - * device - device to layout memory and I/O for. - */ - -static void __init layout_dev(struct pci_dev *dev) -{ - unsigned short cmd; - unsigned int base, mask, size, reg; - unsigned int alignto; - int i; - - /* - * Skip video cards if requested. - */ - - if (((dev->class >> 8 == PCI_CLASS_NOT_DEFINED_VGA) || - (dev->class >> 8 == PCI_CLASS_DISPLAY_VGA) || - (dev->class >> 8 == PCI_CLASS_DISPLAY_XGA)) && skip_vga) - return; - - pci_read_config_word(dev, PCI_COMMAND, &cmd); - - for (reg = PCI_BASE_ADDRESS_0, i = 0; reg <= PCI_BASE_ADDRESS_5; reg += 4, i++) - { - /* - * Figure out how much space and of what type this - * device wants. - */ - - pci_write_config_dword(dev, reg, 0xffffffff); - pci_read_config_dword(dev, reg, &base); - - if (!base) - { - /* this base-address register is unused */ - dev->resource[i].start = 0; - dev->resource[i].end = 0; - dev->resource[i].flags = 0; - continue; - } - - /* - * We've read the base address register back after - * writing all ones and so now we must decode it. - */ - - if (base & PCI_BASE_ADDRESS_SPACE_IO) - { - /* - * I/O space base address register. - */ - - cmd |= PCI_COMMAND_IO; - - base &= PCI_BASE_ADDRESS_IO_MASK; - mask = (~base << 1) | 0x1; - size = (mask & base) & 0xffffffff; - - /* - * Align to multiple of size of minimum base. - */ - - alignto = max_t(unsigned int, 0x040, size); - base = ALIGN(io_base, alignto); - io_base = base + size; - pci_write_config_dword(dev, reg, base | PCI_BASE_ADDRESS_SPACE_IO); - - dev->resource[i].start = base; - dev->resource[i].end = dev->resource[i].start + size - 1; - dev->resource[i].flags = IORESOURCE_IO | PCI_BASE_ADDRESS_SPACE_IO; - - DBG_DEVS(("layout_dev: IO address: %lX\n", base)); - } - else - { - unsigned int type; - - /* - * Memory space base address register. - */ - - cmd |= PCI_COMMAND_MEMORY; - type = base & PCI_BASE_ADDRESS_MEM_TYPE_MASK; - base &= PCI_BASE_ADDRESS_MEM_MASK; - mask = (~base << 1) | 0x1; - size = (mask & base) & 0xffffffff; - switch (type) - { - case PCI_BASE_ADDRESS_MEM_TYPE_32: - case PCI_BASE_ADDRESS_MEM_TYPE_64: - break; - - case PCI_BASE_ADDRESS_MEM_TYPE_1M: - printk("bios32 WARNING: slot %d, function %d " - "requests memory below 1MB---don't " - "know how to do that.\n", - PCI_SLOT(dev->devfn), - PCI_FUNC(dev->devfn)); - continue; - } - - /* - * Align to multiple of size of minimum base. - */ - - alignto = max_t(unsigned int, 0x1000, size); - base = ALIGN(mem_base, alignto); - mem_base = base + size; - pci_write_config_dword(dev, reg, base); - - dev->resource[i].start = base; - dev->resource[i].end = dev->resource[i].start + size - 1; - dev->resource[i].flags = IORESOURCE_MEM; - - if (type == PCI_BASE_ADDRESS_MEM_TYPE_64) - { - /* - * 64-bit address, set the highest 32 bits - * to zero. - */ - - reg += 4; - pci_write_config_dword(dev, reg, 0); - - i++; - dev->resource[i].start = 0; - dev->resource[i].end = 0; - dev->resource[i].flags = 0; - } - } - } - - /* - * Enable device: - */ - - if (dev->class >> 8 == PCI_CLASS_NOT_DEFINED || - dev->class >> 8 == PCI_CLASS_NOT_DEFINED_VGA || - dev->class >> 8 == PCI_CLASS_DISPLAY_VGA || - dev->class >> 8 == PCI_CLASS_DISPLAY_XGA) - { - /* - * All of these (may) have I/O scattered all around - * and may not use i/o-base address registers at all. - * So we just have to always enable I/O to these - * devices. - */ - cmd |= PCI_COMMAND_IO; - } - - pci_write_config_word(dev, PCI_COMMAND, cmd | PCI_COMMAND_MASTER); - - pci_write_config_byte(dev, PCI_LATENCY_TIMER, (disable_pci_burst) ? 0 : 32); - - if (bus_info != NULL) - bus_info->conf_device(dev); /* Machine dependent configuration. */ - - DBG_DEVS(("layout_dev: bus %d slot 0x%x VID 0x%x DID 0x%x class 0x%x\n", - dev->bus->number, PCI_SLOT(dev->devfn), dev->vendor, dev->device, dev->class)); -} - -/* - * static void layout_bus(struct pci_bus *bus) - * - * Layout memory and I/O for all devices on the given bus. - * - * Parameters: - * - * bus - bus. - */ - -static void __init layout_bus(struct pci_bus *bus) -{ - unsigned int bio, bmem; - struct pci_dev *dev; - - DBG_DEVS(("layout_bus: starting bus %d\n", bus->number)); - - if (!bus->devices && !bus->children) - return; - - /* - * Align the current bases on appropriate boundaries (4K for - * IO and 1MB for memory). - */ - - bio = io_base = ALIGN(io_base, 4*KB); - bmem = mem_base = ALIGN(mem_base, 1*MB); - - /* - * PCI devices might have been setup by a PCI BIOS emulation - * running under TOS. In these cases there is a - * window during which two devices may have an overlapping - * address range. To avoid this causing trouble, we first - * turn off the I/O and memory address decoders for all PCI - * devices. They'll be re-enabled only once all address - * decoders are programmed consistently. - */ - - DBG_DEVS(("layout_bus: disable_dev for bus %d\n", bus->number)); - - for (dev = bus->devices; dev; dev = dev->sibling) - { - if ((dev->class >> 16 != PCI_BASE_CLASS_BRIDGE) || - (dev->class >> 8 == PCI_CLASS_BRIDGE_PCMCIA)) - disable_dev(dev); - } - - /* - * Allocate space to each device: - */ - - DBG_DEVS(("layout_bus: starting bus %d devices\n", bus->number)); - - for (dev = bus->devices; dev; dev = dev->sibling) - { - if ((dev->class >> 16 != PCI_BASE_CLASS_BRIDGE) || - (dev->class >> 8 == PCI_CLASS_BRIDGE_PCMCIA)) - layout_dev(dev); - } - - DBG_DEVS(("layout_bus: bus %d finished\n", bus->number)); -} - -/* - * static void pcibios_fixup(void) - * - * Layout memory and I/O of all devices on the PCI bus if 'pci_modify' is - * true. This might be necessary because not every m68k machine with a PCI - * bus has a PCI BIOS. This function should be called right after - * pci_scan_bus() in pcibios_init(). - */ - -static void __init pcibios_fixup(void) -{ - if (pci_modify) - { - /* - * Set base addresses for allocation of I/O and memory space. - */ - - io_base = bus_info->io_space.start + IO_ALLOC_OFFSET; - mem_base = bus_info->mem_space.start + MEM_ALLOC_OFFSET; - - /* - * Scan the tree, allocating PCI memory and I/O space. - */ - - layout_bus(pci_bus_b(pci_root.next)); - } - - /* - * Fix interrupt assignments, etc. - */ - - bus_info->fixup(pci_modify); -} - -/* - * static void pcibios_claim_resources(struct pci_bus *bus) - * - * Claim all resources that are assigned to devices on the given bus. - * - * Parameters: - * - * bus - bus. - */ - -static void __init pcibios_claim_resources(struct pci_bus *bus) -{ - struct pci_dev *dev; - int i; - - while (bus) - { - for (dev = bus->devices; (dev != NULL); dev = dev->sibling) - { - for (i = 0; i < PCI_NUM_RESOURCES; i++) - { - struct resource *r = &dev->resource[i]; - struct resource *pr; - struct pci_bus_info *bus_info = (struct pci_bus_info *) dev->sysdata; - - if ((r->start == 0) || (r->parent != NULL)) - continue; -#if 1 - if (r->flags & IORESOURCE_IO) - pr = &bus_info->io_space; - else - pr = &bus_info->mem_space; -#else - if (r->flags & IORESOURCE_IO) - pr = &ioport_resource; - else - pr = &iomem_resource; -#endif - if (request_resource(pr, r) < 0) - { - printk(KERN_ERR "PCI: Address space collision on region %d of device %s\n", i, dev->name); - } - } - } - - if (bus->children) - pcibios_claim_resources(bus->children); - - bus = bus->next; - } -} - -/* - * int pcibios_assign_resource(struct pci_dev *dev, int i) - * - * Assign a new address to a PCI resource. - * - * Parameters: - * - * dev - device. - * i - resource. - * - * Result: 0 if successful. - */ - -int __init pcibios_assign_resource(struct pci_dev *dev, int i) -{ - struct resource *r = &dev->resource[i]; - struct resource *pr = pci_find_parent_resource(dev, r); - unsigned long size = r->end + 1; - - if (!pr) - return -EINVAL; - - if (r->flags & IORESOURCE_IO) - { - if (size > 0x100) - return -EFBIG; - - if (allocate_resource(pr, r, size, bus_info->io_space.start + - IO_ALLOC_OFFSET, bus_info->io_space.end, 1024)) - return -EBUSY; - } - else - { - if (allocate_resource(pr, r, size, bus_info->mem_space.start + - MEM_ALLOC_OFFSET, bus_info->mem_space.end, size)) - return -EBUSY; - } - - if (i < 6) - pci_write_config_dword(dev, PCI_BASE_ADDRESS_0 + 4 * i, r->start); - - return 0; -} - -void __init pcibios_fixup_bus(struct pci_bus *bus) -{ - struct pci_dev *dev; - void *sysdata; - - sysdata = (bus->parent) ? bus->parent->sysdata : bus->sysdata; - - for (dev = bus->devices; (dev != NULL); dev = dev->sibling) - dev->sysdata = sysdata; -} - -void __init pcibios_init(void) -{ - printk("Linux/m68k PCI BIOS32 revision %x.%02x\n", MAJOR_REV, MINOR_REV); - - bus_info = NULL; -#ifdef CONFIG_HADES - if (MACH_IS_HADES) - bus_info = init_hades_pci(); -#endif - if (bus_info != NULL) - { - printk("PCI: Probing PCI hardware\n"); - pci_scan_bus(0, bus_info->m68k_pci_ops, bus_info); - pcibios_fixup(); - pcibios_claim_resources(pci_root); - } - else - printk("PCI: No PCI bus detected\n"); -} - -char * __init pcibios_setup(char *str) -{ - if (!strcmp(str, "nomodify")) - { - pci_modify = 0; - return NULL; - } - else if (!strcmp(str, "skipvga")) - { - skip_vga = 1; - return NULL; - } - else if (!strcmp(str, "noburst")) - { - disable_pci_burst = 1; - return NULL; - } - - return str; -} -#endif /* CONFIG_PCI */ diff --git a/arch/m68k/kernel/dma.c b/arch/m68k/kernel/dma.c index 6f8c080dd9f9..2bb4245404d8 100644 --- a/arch/m68k/kernel/dma.c +++ b/arch/m68k/kernel/dma.c @@ -66,8 +66,8 @@ void dma_free_coherent(struct device *dev, size_t size, } EXPORT_SYMBOL(dma_free_coherent); -inline void dma_sync_single_for_device(struct device *dev, dma_addr_t handle, size_t size, - enum dma_data_direction dir) +void dma_sync_single_for_device(struct device *dev, dma_addr_t handle, + size_t size, enum dma_data_direction dir) { switch (dir) { case DMA_TO_DEVICE: diff --git a/arch/m68k/kernel/ints.c b/arch/m68k/kernel/ints.c index ded7dd2f67b2..7e8a0d394e61 100644 --- a/arch/m68k/kernel/ints.c +++ b/arch/m68k/kernel/ints.c @@ -429,8 +429,9 @@ int show_interrupts(struct seq_file *p, void *v) return 0; } +#ifdef CONFIG_PROC_FS void init_irq_proc(void) { /* Insert /proc/irq driver here */ } - +#endif diff --git a/arch/m68k/kernel/process.c b/arch/m68k/kernel/process.c index 7888cdf91f5d..3042c2bc8c58 100644 --- a/arch/m68k/kernel/process.c +++ b/arch/m68k/kernel/process.c @@ -78,7 +78,7 @@ unsigned long thread_saved_pc(struct task_struct *tsk) static void default_idle(void) { if (!need_resched()) -#if defined(MACH_ATARI_ONLY) && !defined(CONFIG_HADES) +#if defined(MACH_ATARI_ONLY) /* block out HSYNC on the atari (falcon) */ __asm__("stop #0x2200" : : : "cc"); #else diff --git a/arch/m68k/kernel/traps.c b/arch/m68k/kernel/traps.c index 75b8340b254b..6d813de2baf1 100644 --- a/arch/m68k/kernel/traps.c +++ b/arch/m68k/kernel/traps.c @@ -883,8 +883,7 @@ void show_trace(unsigned long *stack) if (i % 5 == 0) printk("\n "); #endif - printk(" [<%08lx>]", addr); - print_symbol(" %s\n", addr); + printk(" [<%08lx>] %pS\n", addr, (void *)addr); i++; } } @@ -900,10 +899,8 @@ void show_registers(struct pt_regs *regs) int i; print_modules(); - printk("PC: [<%08lx>]",regs->pc); - print_symbol(" %s", regs->pc); - printk("\nSR: %04x SP: %p a2: %08lx\n", - regs->sr, regs, regs->a2); + printk("PC: [<%08lx>] %pS\n", regs->pc, (void *)regs->pc); + printk("SR: %04x SP: %p a2: %08lx\n", regs->sr, regs, regs->a2); printk("d0: %08lx d1: %08lx d2: %08lx d3: %08lx\n", regs->d0, regs->d1, regs->d2, regs->d3); printk("d4: %08lx d5: %08lx a0: %08lx a1: %08lx\n", diff --git a/arch/m68k/kernel/vmlinux-std.lds b/arch/m68k/kernel/vmlinux-std.lds index 99b0784c0552..f846d4e3e5e1 100644 --- a/arch/m68k/kernel/vmlinux-std.lds +++ b/arch/m68k/kernel/vmlinux-std.lds @@ -34,10 +34,10 @@ SECTIONS CONSTRUCTORS } - .bss : { *(.bss) } /* BSS */ - . = ALIGN(16); - .data.cacheline_aligned : { *(.data.cacheline_aligned) } :data + .data.cacheline_aligned : { *(.data.cacheline_aligned) } + + .bss : { *(.bss) } /* BSS */ _edata = .; /* End of data section */ @@ -48,7 +48,7 @@ SECTIONS _sinittext = .; INIT_TEXT _einittext = .; - } + } :data .init.data : { INIT_DATA } . = ALIGN(16); __setup_start = .; @@ -74,6 +74,7 @@ SECTIONS .init.ramfs : { *(.init.ramfs) } __initramfs_end = .; #endif + NOTES . = ALIGN(8192); __init_end = .; diff --git a/arch/m68k/mm/kmap.c b/arch/m68k/mm/kmap.c index 46b7d6035aab..df620ac2a296 100644 --- a/arch/m68k/mm/kmap.c +++ b/arch/m68k/mm/kmap.c @@ -66,8 +66,10 @@ static struct vm_struct *get_io_area(unsigned long size) for (p = &iolist; (tmp = *p) ; p = &tmp->next) { if (size + addr < (unsigned long)tmp->addr) break; - if (addr > KMAP_END-size) + if (addr > KMAP_END-size) { + kfree(area); return NULL; + } addr = tmp->size + (unsigned long)tmp->addr; } area->addr = (void *)addr; diff --git a/arch/m68k/mvme16x/rtc.c b/arch/m68k/mvme16x/rtc.c index 432a9f13b2ed..cea5e3e4e636 100644 --- a/arch/m68k/mvme16x/rtc.c +++ b/arch/m68k/mvme16x/rtc.c @@ -52,15 +52,15 @@ static int rtc_ioctl(struct inode *inode, struct file *file, unsigned int cmd, /* Ensure clock and real-time-mode-register are accessible */ rtc->ctrl = RTC_READ; memset(&wtime, 0, sizeof(struct rtc_time)); - wtime.tm_sec = BCD2BIN(rtc->bcd_sec); - wtime.tm_min = BCD2BIN(rtc->bcd_min); - wtime.tm_hour = BCD2BIN(rtc->bcd_hr); - wtime.tm_mday = BCD2BIN(rtc->bcd_dom); - wtime.tm_mon = BCD2BIN(rtc->bcd_mth)-1; - wtime.tm_year = BCD2BIN(rtc->bcd_year); + wtime.tm_sec = bcd2bin(rtc->bcd_sec); + wtime.tm_min = bcd2bin(rtc->bcd_min); + wtime.tm_hour = bcd2bin(rtc->bcd_hr); + wtime.tm_mday = bcd2bin(rtc->bcd_dom); + wtime.tm_mon = bcd2bin(rtc->bcd_mth)-1; + wtime.tm_year = bcd2bin(rtc->bcd_year); if (wtime.tm_year < 70) wtime.tm_year += 100; - wtime.tm_wday = BCD2BIN(rtc->bcd_dow)-1; + wtime.tm_wday = bcd2bin(rtc->bcd_dow)-1; rtc->ctrl = 0; local_irq_restore(flags); return copy_to_user(argp, &wtime, sizeof wtime) ? @@ -104,12 +104,12 @@ static int rtc_ioctl(struct inode *inode, struct file *file, unsigned int cmd, local_irq_save(flags); rtc->ctrl = RTC_WRITE; - rtc->bcd_sec = BIN2BCD(sec); - rtc->bcd_min = BIN2BCD(min); - rtc->bcd_hr = BIN2BCD(hrs); - rtc->bcd_dom = BIN2BCD(day); - rtc->bcd_mth = BIN2BCD(mon); - rtc->bcd_year = BIN2BCD(yrs%100); + rtc->bcd_sec = bin2bcd(sec); + rtc->bcd_min = bin2bcd(min); + rtc->bcd_hr = bin2bcd(hrs); + rtc->bcd_dom = bin2bcd(day); + rtc->bcd_mth = bin2bcd(mon); + rtc->bcd_year = bin2bcd(yrs%100); rtc->ctrl = 0; local_irq_restore(flags); diff --git a/arch/m68k/q40/config.c b/arch/m68k/q40/config.c index be9de2f3dc48..9c7eefa3f98a 100644 --- a/arch/m68k/q40/config.c +++ b/arch/m68k/q40/config.c @@ -23,6 +23,7 @@ #include <linux/serial_reg.h> #include <linux/rtc.h> #include <linux/vt_kern.h> +#include <linux/bcd.h> #include <asm/io.h> #include <asm/rtc.h> @@ -216,17 +217,6 @@ int q40_parse_bootinfo(const struct bi_record *rec) } -static inline unsigned char bcd2bin(unsigned char b) -{ - return (b >> 4) * 10 + (b & 15); -} - -static inline unsigned char bin2bcd(unsigned char b) -{ - return (b / 10) * 16 + (b % 10); -} - - static unsigned long q40_gettimeoffset(void) { return 5000 * (ql_ticks != 0); diff --git a/arch/m68k/sun3x/time.c b/arch/m68k/sun3x/time.c index f5eaafb00d21..536a04aaf22f 100644 --- a/arch/m68k/sun3x/time.c +++ b/arch/m68k/sun3x/time.c @@ -47,23 +47,23 @@ int sun3x_hwclk(int set, struct rtc_time *t) if(set) { h->csr |= C_WRITE; - h->sec = BIN2BCD(t->tm_sec); - h->min = BIN2BCD(t->tm_min); - h->hour = BIN2BCD(t->tm_hour); - h->wday = BIN2BCD(t->tm_wday); - h->mday = BIN2BCD(t->tm_mday); - h->month = BIN2BCD(t->tm_mon); - h->year = BIN2BCD(t->tm_year); + h->sec = bin2bcd(t->tm_sec); + h->min = bin2bcd(t->tm_min); + h->hour = bin2bcd(t->tm_hour); + h->wday = bin2bcd(t->tm_wday); + h->mday = bin2bcd(t->tm_mday); + h->month = bin2bcd(t->tm_mon); + h->year = bin2bcd(t->tm_year); h->csr &= ~C_WRITE; } else { h->csr |= C_READ; - t->tm_sec = BCD2BIN(h->sec); - t->tm_min = BCD2BIN(h->min); - t->tm_hour = BCD2BIN(h->hour); - t->tm_wday = BCD2BIN(h->wday); - t->tm_mday = BCD2BIN(h->mday); - t->tm_mon = BCD2BIN(h->month); - t->tm_year = BCD2BIN(h->year); + t->tm_sec = bcd2bin(h->sec); + t->tm_min = bcd2bin(h->min); + t->tm_hour = bcd2bin(h->hour); + t->tm_wday = bcd2bin(h->wday); + t->tm_mday = bcd2bin(h->mday); + t->tm_mon = bcd2bin(h->month); + t->tm_year = bcd2bin(h->year); h->csr &= ~C_READ; } diff --git a/arch/x86/kernel/rtc.c b/arch/x86/kernel/rtc.c index 05191bbc68b8..0a23b5795b25 100644 --- a/arch/x86/kernel/rtc.c +++ b/arch/x86/kernel/rtc.c @@ -223,11 +223,25 @@ static struct platform_device rtc_device = { static __init int add_rtc_cmos(void) { #ifdef CONFIG_PNP - if (!pnp_platform_devices) - platform_device_register(&rtc_device); -#else + static const char *ids[] __initconst = + { "PNP0b00", "PNP0b01", "PNP0b02", }; + struct pnp_dev *dev; + struct pnp_id *id; + int i; + + pnp_for_each_dev(dev) { + for (id = dev->id; id; id = id->next) { + for (i = 0; i < ARRAY_SIZE(ids); i++) { + if (compare_pnp_id(id, ids[i]) != 0) + return 0; + } + } + } +#endif + platform_device_register(&rtc_device); -#endif /* CONFIG_PNP */ + dev_info(&rtc_device.dev, + "registered platform RTC device (no PNP device found)\n"); return 0; } device_initcall(add_rtc_cmos); diff --git a/drivers/acpi/glue.c b/drivers/acpi/glue.c index 3c578ef78c48..24649ada08df 100644 --- a/drivers/acpi/glue.c +++ b/drivers/acpi/glue.c @@ -260,115 +260,3 @@ static int __init init_acpi_device_notify(void) } arch_initcall(init_acpi_device_notify); - - -#if defined(CONFIG_RTC_DRV_CMOS) || defined(CONFIG_RTC_DRV_CMOS_MODULE) - -#ifdef CONFIG_PM -static u32 rtc_handler(void *context) -{ - acpi_clear_event(ACPI_EVENT_RTC); - acpi_disable_event(ACPI_EVENT_RTC, 0); - return ACPI_INTERRUPT_HANDLED; -} - -static inline void rtc_wake_setup(void) -{ - acpi_install_fixed_event_handler(ACPI_EVENT_RTC, rtc_handler, NULL); - /* - * After the RTC handler is installed, the Fixed_RTC event should - * be disabled. Only when the RTC alarm is set will it be enabled. - */ - acpi_clear_event(ACPI_EVENT_RTC); - acpi_disable_event(ACPI_EVENT_RTC, 0); -} - -static void rtc_wake_on(struct device *dev) -{ - acpi_clear_event(ACPI_EVENT_RTC); - acpi_enable_event(ACPI_EVENT_RTC, 0); -} - -static void rtc_wake_off(struct device *dev) -{ - acpi_disable_event(ACPI_EVENT_RTC, 0); -} -#else -#define rtc_wake_setup() do{}while(0) -#define rtc_wake_on NULL -#define rtc_wake_off NULL -#endif - -/* Every ACPI platform has a mc146818 compatible "cmos rtc". Here we find - * its device node and pass extra config data. This helps its driver use - * capabilities that the now-obsolete mc146818 didn't have, and informs it - * that this board's RTC is wakeup-capable (per ACPI spec). - */ -#include <linux/mc146818rtc.h> - -static struct cmos_rtc_board_info rtc_info; - - -/* PNP devices are registered in a subsys_initcall(); - * ACPI specifies the PNP IDs to use. - */ -#include <linux/pnp.h> - -static int __init pnp_match(struct device *dev, void *data) -{ - static const char *ids[] = { "PNP0b00", "PNP0b01", "PNP0b02", }; - struct pnp_dev *pnp = to_pnp_dev(dev); - int i; - - for (i = 0; i < ARRAY_SIZE(ids); i++) { - if (compare_pnp_id(pnp->id, ids[i]) != 0) - return 1; - } - return 0; -} - -static struct device *__init get_rtc_dev(void) -{ - return bus_find_device(&pnp_bus_type, NULL, NULL, pnp_match); -} - -static int __init acpi_rtc_init(void) -{ - struct device *dev = get_rtc_dev(); - - if (acpi_disabled) - return 0; - - if (dev) { - rtc_wake_setup(); - rtc_info.wake_on = rtc_wake_on; - rtc_info.wake_off = rtc_wake_off; - - /* workaround bug in some ACPI tables */ - if (acpi_gbl_FADT.month_alarm && !acpi_gbl_FADT.day_alarm) { - DBG("bogus FADT month_alarm\n"); - acpi_gbl_FADT.month_alarm = 0; - } - - rtc_info.rtc_day_alarm = acpi_gbl_FADT.day_alarm; - rtc_info.rtc_mon_alarm = acpi_gbl_FADT.month_alarm; - rtc_info.rtc_century = acpi_gbl_FADT.century; - - /* NOTE: S4_RTC_WAKE is NOT currently useful to Linux */ - if (acpi_gbl_FADT.flags & ACPI_FADT_S4_RTC_WAKE) - printk(PREFIX "RTC can wake from S4\n"); - - - dev->platform_data = &rtc_info; - - /* RTC always wakes from S1/S2/S3, and often S4/STD */ - device_init_wakeup(dev, 1); - - put_device(dev); - } else - DBG("RTC unavailable?\n"); - return 0; -} -module_init(acpi_rtc_init); - -#endif diff --git a/drivers/block/ataflop.c b/drivers/block/ataflop.c index 49f274197b16..432cf4018291 100644 --- a/drivers/block/ataflop.c +++ b/drivers/block/ataflop.c @@ -1882,10 +1882,6 @@ static int __init atari_floppy_init (void) /* Amiga, Mac, ... don't have Atari-compatible floppy :-) */ return -ENODEV; - if (MACH_IS_HADES) - /* Hades doesn't have Atari-compatible floppy */ - return -ENODEV; - if (register_blkdev(FLOPPY_MAJOR,"fd")) return -EBUSY; diff --git a/drivers/char/vt.c b/drivers/char/vt.c index 57029fefd64a..a0f7ffb68087 100644 --- a/drivers/char/vt.c +++ b/drivers/char/vt.c @@ -301,7 +301,7 @@ static void scrup(struct vc_data *vc, unsigned int t, unsigned int b, int nr) d = (unsigned short *)(vc->vc_origin + vc->vc_size_row * t); s = (unsigned short *)(vc->vc_origin + vc->vc_size_row * (t + nr)); scr_memmovew(d, s, (b - t - nr) * vc->vc_size_row); - scr_memsetw(d + (b - t - nr) * vc->vc_cols, vc->vc_scrl_erase_char, + scr_memsetw(d + (b - t - nr) * vc->vc_cols, vc->vc_video_erase_char, vc->vc_size_row * nr); } @@ -319,7 +319,7 @@ static void scrdown(struct vc_data *vc, unsigned int t, unsigned int b, int nr) s = (unsigned short *)(vc->vc_origin + vc->vc_size_row * t); step = vc->vc_cols * nr; scr_memmovew(s + step, s, (b - t - nr) * vc->vc_size_row); - scr_memsetw(s, vc->vc_scrl_erase_char, 2 * step); + scr_memsetw(s, vc->vc_video_erase_char, 2 * step); } static void do_update_region(struct vc_data *vc, unsigned long start, int count) @@ -434,7 +434,6 @@ static void update_attr(struct vc_data *vc) vc->vc_blink, vc->vc_underline, vc->vc_reverse ^ vc->vc_decscnm, vc->vc_italic); vc->vc_video_erase_char = (build_attr(vc, vc->vc_color, 1, vc->vc_blink, 0, vc->vc_decscnm, 0) << 8) | ' '; - vc->vc_scrl_erase_char = (build_attr(vc, vc->vc_def_color, 1, false, false, vc->vc_decscnm, false) << 8) | ' '; } /* Note: inverting the screen twice should revert to the original state */ diff --git a/drivers/hid/Kconfig b/drivers/hid/Kconfig index cacf89e65af4..da64108de775 100644 --- a/drivers/hid/Kconfig +++ b/drivers/hid/Kconfig @@ -17,6 +17,25 @@ config HID tristate "Generic HID support" depends on INPUT default y + select HID_A4TECH if !EMBEDDED + select HID_APPLE if !EMBEDDED + select HID_BELKIN if !EMBEDDED + select HID_BRIGHT if !EMBEDDED + select HID_CHERRY if !EMBEDDED + select HID_CHICONY if !EMBEDDED + select HID_CYPRESS if !EMBEDDED + select HID_DELL if !EMBEDDED + select HID_EZKEY if !EMBEDDED + select HID_GYRATION if !EMBEDDED + select HID_LOGITECH if !EMBEDDED + select HID_MICROSOFT if !EMBEDDED + select HID_MONTEREY if !EMBEDDED + select HID_PANTHERLORD if !EMBEDDED + select HID_PETALYNX if !EMBEDDED + select HID_SAMSUNG if !EMBEDDED + select HID_SONY if !EMBEDDED + select HID_SUNPLUS if !EMBEDDED + ---help--- A human interface device (HID) is a type of computer device that interacts directly with and takes input from humans. The term "HID" @@ -67,4 +86,206 @@ config HIDRAW source "drivers/hid/usbhid/Kconfig" +menu "Special HID drivers" + depends on HID + +config HID_COMPAT + bool "Load all HID drivers on hid core load" + default y + ---help--- + Compatible option for older userspace. If you have system without udev + support of module loading through aliases and also old + module-init-tools which can't handle hid bus, choose Y here. Otherwise + say N. If you say N and your userspace is old enough, the only + functionality you lose is modules autoloading. + + If unsure, say Y. + +config HID_A4TECH + tristate "A4 tech" + default m + depends on USB_HID + ---help--- + Support for A4 tech X5 and WOP-35 / Trust 450L mice. + +config HID_APPLE + tristate "Apple" + default m + depends on (USB_HID || BT_HIDP) + ---help--- + Support for some Apple devices which less or more break + HID specification. + + Say Y here if you want support for the special keys (Fn, Numlock) on + Apple iBooks, PowerBooks, MacBooks, MacBook Pros and aluminum USB + keyboards. + + If unsure, say M. + +config HID_BELKIN + tristate "Belkin" + default m + depends on USB_HID + ---help--- + Support for Belkin Flip KVM and Wireless keyboard. + +config HID_BRIGHT + tristate "Bright" + default m + depends on USB_HID + ---help--- + Support for Bright ABNT-2 keyboard. + +config HID_CHERRY + tristate "Cherry" + default m + depends on USB_HID + ---help--- + Support for Cherry Cymotion. + +config HID_CHICONY + tristate "Chicony" + default m + depends on USB_HID + ---help--- + Support for Chicony Tactical pad. + +config HID_CYPRESS + tristate "Cypress" + default m + depends on USB_HID + ---help--- + Support for Cypress mouse and barcodes. + +config HID_DELL + tristate "Dell" + default m + depends on USB_HID + ---help--- + Support for Dell W7658. + +config HID_EZKEY + tristate "Ezkey" + default m + depends on USB_HID + ---help--- + Support for Ezkey mouse and barcodes. + +config HID_GYRATION + tristate "Gyration" + default m + depends on USB_HID + ---help--- + Support for Gyration remote. + +config HID_LOGITECH + tristate "Logitech" + default m + depends on USB_HID + ---help--- + Support for some Logitech devices which breaks less or more + HID specification. + +config LOGITECH_FF + bool "Logitech force feedback" + depends on HID_LOGITECH + select INPUT_FF_MEMLESS + help + Say Y here if you have one of these devices: + - Logitech WingMan Cordless RumblePad + - Logitech WingMan Cordless RumblePad 2 + - Logitech WingMan Force 3D + - Logitech Formula Force EX + - Logitech MOMO Force wheel + + and if you want to enable force feedback for them. + Note: if you say N here, this device will still be supported, but without + force feedback. + +config LOGIRUMBLEPAD2_FF + bool "Logitech Rumblepad 2 force feedback" + depends on HID_LOGITECH + select INPUT_FF_MEMLESS + help + Say Y here if you want to enable force feedback support for Logitech + Rumblepad 2 devices. + +config HID_MICROSOFT + tristate "Microsoft" + default m + depends on USB_HID + ---help--- + Support for some Microsoft devices which breaks less or more + HID specification. + +config HID_MONTEREY + tristate "Monterey" + default m + depends on USB_HID + ---help--- + Support for Monterey Genius KB29E. + +config HID_PANTHERLORD + tristate "Pantherlord devices support" + default m + depends on USB_HID + ---help--- + Support for PantherLord/GreenAsia based device support. + + +config PANTHERLORD_FF + bool "Pantherlord force feedback support" + depends on HID_PANTHERLORD + select INPUT_FF_MEMLESS + help + Say Y here if you have a PantherLord/GreenAsia based game controller + or adapter and want to enable force feedback support for it. + +config HID_PETALYNX + tristate "Petalynx" + default m + depends on USB_HID + ---help--- + Support for Petalynx Maxter remote. + +config HID_SAMSUNG + tristate "Samsung" + default m + depends on USB_HID + ---help--- + Support for Samsung IR remote. + +config HID_SONY + tristate "Sony" + default m + depends on USB_HID + ---help--- + Support for Sony PS3 controller. + +config HID_SUNPLUS + tristate "Sunplus" + default m + depends on USB_HID + ---help--- + Support for Sunplus WDesktop input device. + +config THRUSTMASTER_FF + tristate "ThrustMaster devices support" + default m + depends on USB_HID + select INPUT_FF_MEMLESS + help + Say Y here if you have a THRUSTMASTER FireStore Dual Power 2 or + a THRUSTMASTER Ferrari GT Rumble Force or Force Feedback Wheel. + +config ZEROPLUS_FF + tristate "Zeroplus based game controller support" + default m + depends on USB_HID + select INPUT_FF_MEMLESS + help + Say Y here if you have a Zeroplus based game controller. + +endmenu + endif # HID_SUPPORT diff --git a/drivers/hid/Makefile b/drivers/hid/Makefile index 275dc522c738..b09e43e7413e 100644 --- a/drivers/hid/Makefile +++ b/drivers/hid/Makefile @@ -1,13 +1,46 @@ # # Makefile for the HID driver # -hid-objs := hid-core.o hid-input.o hid-input-quirks.o +hid-objs := hid-core.o hid-input.o obj-$(CONFIG_HID) += hid.o hid-$(CONFIG_HID_DEBUG) += hid-debug.o hid-$(CONFIG_HIDRAW) += hidraw.o +ifdef CONFIG_HID_COMPAT +obj-m += hid-dummy.o +endif + +hid-logitech-objs := hid-lg.o +ifdef CONFIG_LOGITECH_FF + hid-logitech-objs += hid-lgff.o +endif +ifdef CONFIG_LOGIRUMBLEPAD2_FF + hid-logitech-objs += hid-lg2ff.o +endif + +obj-$(CONFIG_HID_A4TECH) += hid-a4tech.o +obj-$(CONFIG_HID_APPLE) += hid-apple.o +obj-$(CONFIG_HID_BELKIN) += hid-belkin.o +obj-$(CONFIG_HID_BRIGHT) += hid-bright.o +obj-$(CONFIG_HID_CHERRY) += hid-cherry.o +obj-$(CONFIG_HID_CHICONY) += hid-chicony.o +obj-$(CONFIG_HID_CYPRESS) += hid-cypress.o +obj-$(CONFIG_HID_DELL) += hid-dell.o +obj-$(CONFIG_HID_EZKEY) += hid-ezkey.o +obj-$(CONFIG_HID_GYRATION) += hid-gyration.o +obj-$(CONFIG_HID_LOGITECH) += hid-logitech.o +obj-$(CONFIG_HID_MICROSOFT) += hid-microsoft.o +obj-$(CONFIG_HID_MONTEREY) += hid-monterey.o +obj-$(CONFIG_HID_PANTHERLORD) += hid-pl.o +obj-$(CONFIG_HID_PETALYNX) += hid-petalynx.o +obj-$(CONFIG_HID_SAMSUNG) += hid-samsung.o +obj-$(CONFIG_HID_SONY) += hid-sony.o +obj-$(CONFIG_HID_SUNPLUS) += hid-sunplus.o +obj-$(CONFIG_THRUSTMASTER_FF) += hid-tmff.o +obj-$(CONFIG_ZEROPLUS_FF) += hid-zpff.o + obj-$(CONFIG_USB_HID) += usbhid/ obj-$(CONFIG_USB_MOUSE) += usbhid/ obj-$(CONFIG_USB_KBD) += usbhid/ diff --git a/drivers/hid/hid-a4tech.c b/drivers/hid/hid-a4tech.c new file mode 100644 index 000000000000..ebca00e6c103 --- /dev/null +++ b/drivers/hid/hid-a4tech.c @@ -0,0 +1,162 @@ +/* + * HID driver for some a4tech "special" devices + * + * 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) 2006-2007 Jiri Kosina + * Copyright (c) 2007 Paul Walmsley + * Copyright (c) 2008 Jiri Slaby + */ + +/* + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the Free + * Software Foundation; either version 2 of the License, or (at your option) + * any later version. + */ + +#include <linux/device.h> +#include <linux/input.h> +#include <linux/hid.h> +#include <linux/module.h> + +#include "hid-ids.h" + +#define A4_2WHEEL_MOUSE_HACK_7 0x01 +#define A4_2WHEEL_MOUSE_HACK_B8 0x02 + +struct a4tech_sc { + unsigned long quirks; + unsigned int hw_wheel; + __s32 delayed_value; +}; + +static int a4_input_mapped(struct hid_device *hdev, struct hid_input *hi, + struct hid_field *field, struct hid_usage *usage, + unsigned long **bit, int *max) +{ + struct a4tech_sc *a4 = hid_get_drvdata(hdev); + + if (usage->type == EV_REL && usage->code == REL_WHEEL) + set_bit(REL_HWHEEL, *bit); + + if ((a4->quirks & A4_2WHEEL_MOUSE_HACK_7) && usage->hid == 0x00090007) + return -1; + + return 0; +} + +static int a4_event(struct hid_device *hdev, struct hid_field *field, + struct hid_usage *usage, __s32 value) +{ + struct a4tech_sc *a4 = hid_get_drvdata(hdev); + struct input_dev *input; + + if (!(hdev->claimed & HID_CLAIMED_INPUT) || !field->hidinput || + !usage->type) + return 0; + + input = field->hidinput->input; + + if (a4->quirks & A4_2WHEEL_MOUSE_HACK_B8) { + if (usage->type == EV_REL && usage->code == REL_WHEEL) { + a4->delayed_value = value; + return 1; + } + + if (usage->hid == 0x000100b8) { + input_event(input, EV_REL, value ? REL_HWHEEL : + REL_WHEEL, a4->delayed_value); + return 1; + } + } + + if ((a4->quirks & A4_2WHEEL_MOUSE_HACK_7) && usage->hid == 0x00090007) { + a4->hw_wheel = !!value; + return 1; + } + + if (usage->code == REL_WHEEL && a4->hw_wheel) { + input_event(input, usage->type, REL_HWHEEL, value); + return 1; + } + + return 0; +} + +static int a4_probe(struct hid_device *hdev, const struct hid_device_id *id) +{ + struct a4tech_sc *a4; + int ret; + + a4 = kzalloc(sizeof(*a4), GFP_KERNEL); + if (a4 == NULL) { + dev_err(&hdev->dev, "can't alloc device descriptor\n"); + ret = -ENOMEM; + goto err_free; + } + + a4->quirks = id->driver_data; + + hid_set_drvdata(hdev, a4); + + ret = hid_parse(hdev); + if (ret) { + dev_err(&hdev->dev, "parse failed\n"); + goto err_free; + } + + ret = hid_hw_start(hdev, HID_CONNECT_DEFAULT); + if (ret) { + dev_err(&hdev->dev, "hw start failed\n"); + goto err_free; + } + + return 0; +err_free: + kfree(a4); + return ret; +} + +static void a4_remove(struct hid_device *hdev) +{ + struct a4tech_sc *a4 = hid_get_drvdata(hdev); + + hid_hw_stop(hdev); + kfree(a4); +} + +static const struct hid_device_id a4_devices[] = { + { HID_USB_DEVICE(USB_VENDOR_ID_A4TECH, USB_DEVICE_ID_A4TECH_WCP32PU), + .driver_data = A4_2WHEEL_MOUSE_HACK_7 }, + { HID_USB_DEVICE(USB_VENDOR_ID_A4TECH, USB_DEVICE_ID_A4TECH_X5_005D), + .driver_data = A4_2WHEEL_MOUSE_HACK_B8 }, + { } +}; +MODULE_DEVICE_TABLE(hid, a4_devices); + +static struct hid_driver a4_driver = { + .name = "a4tech", + .id_table = a4_devices, + .input_mapped = a4_input_mapped, + .event = a4_event, + .probe = a4_probe, + .remove = a4_remove, +}; + +static int a4_init(void) +{ + return hid_register_driver(&a4_driver); +} + +static void a4_exit(void) +{ + hid_unregister_driver(&a4_driver); +} + +module_init(a4_init); +module_exit(a4_exit); +MODULE_LICENSE("GPL"); + +HID_COMPAT_LOAD_DRIVER(a4tech); diff --git a/drivers/hid/hid-apple.c b/drivers/hid/hid-apple.c new file mode 100644 index 000000000000..fd7f896b34f7 --- /dev/null +++ b/drivers/hid/hid-apple.c @@ -0,0 +1,484 @@ +/* + * USB HID quirks support for Linux + * + * 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) 2006-2007 Jiri Kosina + * Copyright (c) 2007 Paul Walmsley + * Copyright (c) 2008 Jiri Slaby <jirislaby@gmail.com> + */ + +/* + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the Free + * Software Foundation; either version 2 of the License, or (at your option) + * any later version. + */ + +#include <linux/device.h> +#include <linux/hid.h> +#include <linux/module.h> +#include <linux/usb.h> + +#include "hid-ids.h" + +#define APPLE_RDESC_JIS 0x0001 +#define APPLE_IGNORE_MOUSE 0x0002 +#define APPLE_HAS_FN 0x0004 +#define APPLE_HIDDEV 0x0008 +#define APPLE_ISO_KEYBOARD 0x0010 +#define APPLE_MIGHTYMOUSE 0x0020 +#define APPLE_INVERT_HWHEEL 0x0040 +#define APPLE_IGNORE_HIDINPUT 0x0080 +#define APPLE_NUMLOCK_EMULATION 0x0100 + +#define APPLE_FLAG_FKEY 0x01 + +static unsigned int fnmode = 1; +module_param(fnmode, uint, 0644); +MODULE_PARM_DESC(fnmode, "Mode of fn key on Apple keyboards (0 = disabled, " + "[1] = fkeyslast, 2 = fkeysfirst)"); + +struct apple_sc { + unsigned long quirks; + unsigned int fn_on; + DECLARE_BITMAP(pressed_fn, KEY_CNT); + DECLARE_BITMAP(pressed_numlock, KEY_CNT); +}; + +struct apple_key_translation { + u16 from; + u16 to; + u8 flags; +}; + +static struct apple_key_translation apple_fn_keys[] = { + { KEY_BACKSPACE, KEY_DELETE }, + { KEY_F1, KEY_BRIGHTNESSDOWN, APPLE_FLAG_FKEY }, + { KEY_F2, KEY_BRIGHTNESSUP, APPLE_FLAG_FKEY }, + { KEY_F3, KEY_FN_F5, APPLE_FLAG_FKEY }, /* Exposé */ + { KEY_F4, KEY_FN_F4, APPLE_FLAG_FKEY }, /* Dashboard */ + { KEY_F5, KEY_KBDILLUMDOWN, APPLE_FLAG_FKEY }, + { KEY_F6, KEY_KBDILLUMUP, APPLE_FLAG_FKEY }, + { KEY_F7, KEY_PREVIOUSSONG, APPLE_FLAG_FKEY }, + { KEY_F8, KEY_PLAYPAUSE, APPLE_FLAG_FKEY }, + { KEY_F9, KEY_NEXTSONG, APPLE_FLAG_FKEY }, + { KEY_F10, KEY_MUTE, APPLE_FLAG_FKEY }, + { KEY_F11, KEY_VOLUMEDOWN, APPLE_FLAG_FKEY }, + { KEY_F12, KEY_VOLUMEUP, APPLE_FLAG_FKEY }, + { KEY_UP, KEY_PAGEUP }, + { KEY_DOWN, KEY_PAGEDOWN }, + { KEY_LEFT, KEY_HOME }, + { KEY_RIGHT, KEY_END }, + { } +}; + +static struct apple_key_translation powerbook_fn_keys[] = { + { KEY_BACKSPACE, KEY_DELETE }, + { KEY_F1, KEY_BRIGHTNESSDOWN, APPLE_FLAG_FKEY }, + { KEY_F2, KEY_BRIGHTNESSUP, APPLE_FLAG_FKEY }, + { KEY_F3, KEY_MUTE, APPLE_FLAG_FKEY }, + { KEY_F4, KEY_VOLUMEDOWN, APPLE_FLAG_FKEY }, + { KEY_F5, KEY_VOLUMEUP, APPLE_FLAG_FKEY }, + { KEY_F6, KEY_NUMLOCK, APPLE_FLAG_FKEY }, + { KEY_F7, KEY_SWITCHVIDEOMODE, APPLE_FLAG_FKEY }, + { KEY_F8, KEY_KBDILLUMTOGGLE, APPLE_FLAG_FKEY }, + { KEY_F9, KEY_KBDILLUMDOWN, APPLE_FLAG_FKEY }, + { KEY_F10, KEY_KBDILLUMUP, APPLE_FLAG_FKEY }, + { KEY_UP, KEY_PAGEUP }, + { KEY_DOWN, KEY_PAGEDOWN }, + { KEY_LEFT, KEY_HOME }, + { KEY_RIGHT, KEY_END }, + { } +}; + +static struct apple_key_translation powerbook_numlock_keys[] = { + { KEY_J, KEY_KP1 }, + { KEY_K, KEY_KP2 }, + { KEY_L, KEY_KP3 }, + { KEY_U, KEY_KP4 }, + { KEY_I, KEY_KP5 }, + { KEY_O, KEY_KP6 }, + { KEY_7, KEY_KP7 }, + { KEY_8, KEY_KP8 }, + { KEY_9, KEY_KP9 }, + { KEY_M, KEY_KP0 }, + { KEY_DOT, KEY_KPDOT }, + { KEY_SLASH, KEY_KPPLUS }, + { KEY_SEMICOLON, KEY_KPMINUS }, + { KEY_P, KEY_KPASTERISK }, + { KEY_MINUS, KEY_KPEQUAL }, + { KEY_0, KEY_KPSLASH }, + { KEY_F6, KEY_NUMLOCK }, + { KEY_KPENTER, KEY_KPENTER }, + { KEY_BACKSPACE, KEY_BACKSPACE }, + { } +}; + +static struct apple_key_translation apple_iso_keyboard[] = { + { KEY_GRAVE, KEY_102ND }, + { KEY_102ND, KEY_GRAVE }, + { } +}; + +static struct apple_key_translation *apple_find_translation( + struct apple_key_translation *table, u16 from) +{ + struct apple_key_translation *trans; + + /* Look for the translation */ + for (trans = table; trans->from; trans++) + if (trans->from == from) + return trans; + + return NULL; +} + +static int hidinput_apple_event(struct hid_device *hid, struct input_dev *input, + struct hid_usage *usage, __s32 value) +{ + struct apple_sc *asc = hid_get_drvdata(hid); + struct apple_key_translation *trans; + + if (usage->code == KEY_FN) { + asc->fn_on = !!value; + input_event(input, usage->type, usage->code, value); + return 1; + } + + if (fnmode) { + int do_translate; + + trans = apple_find_translation((hid->product < 0x220 || + hid->product >= 0x300) ? + powerbook_fn_keys : apple_fn_keys, + usage->code); + if (trans) { + if (test_bit(usage->code, asc->pressed_fn)) + do_translate = 1; + else if (trans->flags & APPLE_FLAG_FKEY) + do_translate = (fnmode == 2 && asc->fn_on) || + (fnmode == 1 && !asc->fn_on); + else + do_translate = asc->fn_on; + + if (do_translate) { + if (value) + set_bit(usage->code, asc->pressed_fn); + else + clear_bit(usage->code, asc->pressed_fn); + + input_event(input, usage->type, trans->to, + value); + + return 1; + } + } + + if (asc->quirks & APPLE_NUMLOCK_EMULATION && + (test_bit(usage->code, asc->pressed_numlock) || + test_bit(LED_NUML, input->led))) { + trans = apple_find_translation(powerbook_numlock_keys, + usage->code); + + if (trans) { + if (value) + set_bit(usage->code, + asc->pressed_numlock); + else + clear_bit(usage->code, + asc->pressed_numlock); + + input_event(input, usage->type, trans->to, + value); + } + + return 1; + } + } + + if (asc->quirks & APPLE_ISO_KEYBOARD) { + trans = apple_find_translation(apple_iso_keyboard, usage->code); + if (trans) { + input_event(input, usage->type, trans->to, value); + return 1; + } + } + + return 0; +} + +static int apple_event(struct hid_device *hdev, struct hid_field *field, + struct hid_usage *usage, __s32 value) +{ + struct apple_sc *asc = hid_get_drvdata(hdev); + + if (!(hdev->claimed & HID_CLAIMED_INPUT) || !field->hidinput || + !usage->type) + return 0; + + if ((asc->quirks & APPLE_INVERT_HWHEEL) && + usage->code == REL_HWHEEL) { + input_event(field->hidinput->input, usage->type, usage->code, + -value); + return 1; + } + + if ((asc->quirks & APPLE_HAS_FN) && + hidinput_apple_event(hdev, field->hidinput->input, + usage, value)) + return 1; + + + return 0; +} + +/* + * MacBook JIS keyboard has wrong logical maximum + */ +static void apple_report_fixup(struct hid_device *hdev, __u8 *rdesc, + unsigned int rsize) +{ + struct apple_sc *asc = hid_get_drvdata(hdev); + + if ((asc->quirks & APPLE_RDESC_JIS) && rsize >= 60 && + rdesc[53] == 0x65 && rdesc[59] == 0x65) { + dev_info(&hdev->dev, "fixing up MacBook JIS keyboard report " + "descriptor\n"); + rdesc[53] = rdesc[59] = 0xe7; + } +} + +static void apple_setup_input(struct input_dev *input) +{ + struct apple_key_translation *trans; + + set_bit(KEY_NUMLOCK, input->keybit); + + /* Enable all needed keys */ + for (trans = apple_fn_keys; trans->from; trans++) + set_bit(trans->to, input->keybit); + + for (trans = powerbook_fn_keys; trans->from; trans++) + set_bit(trans->to, input->keybit); + + for (trans = powerbook_numlock_keys; trans->from; trans++) + set_bit(trans->to, input->keybit); + + for (trans = apple_iso_keyboard; trans->from; trans++) + set_bit(trans->to, input->keybit); +} + +static int apple_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_UP_CUSTOM | 0x0003)) { + /* The fn key on Apple USB keyboards */ + set_bit(EV_REP, hi->input->evbit); + hid_map_usage_clear(hi, usage, bit, max, EV_KEY, KEY_FN); + apple_setup_input(hi->input); + return 1; + } + + /* we want the hid layer to go through standard path (set and ignore) */ + return 0; +} + +static int apple_input_mapped(struct hid_device *hdev, struct hid_input *hi, + struct hid_field *field, struct hid_usage *usage, + unsigned long **bit, int *max) +{ + struct apple_sc *asc = hid_get_drvdata(hdev); + + if (asc->quirks & APPLE_MIGHTYMOUSE) { + if (usage->hid == HID_GD_Z) + hid_map_usage(hi, usage, bit, max, EV_REL, REL_HWHEEL); + else if (usage->code == BTN_1) + hid_map_usage(hi, usage, bit, max, EV_KEY, BTN_2); + else if (usage->code == BTN_2) + hid_map_usage(hi, usage, bit, max, EV_KEY, BTN_1); + } + + return 0; +} + +static int apple_probe(struct hid_device *hdev, + const struct hid_device_id *id) +{ + unsigned long quirks = id->driver_data; + struct apple_sc *asc; + unsigned int connect_mask = HID_CONNECT_DEFAULT; + int ret; + + /* return something else or move to hid layer? device will reside + allocated */ + if (id->bus == BUS_USB && (quirks & APPLE_IGNORE_MOUSE) && + to_usb_interface(hdev->dev.parent)->cur_altsetting-> + desc.bInterfaceProtocol == USB_INTERFACE_PROTOCOL_MOUSE) + return -ENODEV; + + asc = kzalloc(sizeof(*asc), GFP_KERNEL); + if (asc == NULL) { + dev_err(&hdev->dev, "can't alloc apple descriptor\n"); + return -ENOMEM; + } + + asc->quirks = quirks; + + hid_set_drvdata(hdev, asc); + + ret = hid_parse(hdev); + if (ret) { + dev_err(&hdev->dev, "parse failed\n"); + goto err_free; + } + + if (quirks & APPLE_HIDDEV) + connect_mask |= HID_CONNECT_HIDDEV_FORCE; + if (quirks & APPLE_IGNORE_HIDINPUT) + connect_mask &= ~HID_CONNECT_HIDINPUT; + + ret = hid_hw_start(hdev, connect_mask); + if (ret) { + dev_err(&hdev->dev, "hw start failed\n"); + goto err_free; + } + + return 0; +err_free: + kfree(asc); + return ret; +} + +static void apple_remove(struct hid_device *hdev) +{ + hid_hw_stop(hdev); + kfree(hid_get_drvdata(hdev)); +} + +static const struct hid_device_id apple_devices[] = { + { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_ATV_IRCONTROL), + .driver_data = APPLE_HIDDEV | APPLE_IGNORE_HIDINPUT }, + { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_IRCONTROL4), + .driver_data = APPLE_HIDDEV | APPLE_IGNORE_HIDINPUT }, + { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_MIGHTYMOUSE), + .driver_data = APPLE_MIGHTYMOUSE | APPLE_INVERT_HWHEEL }, + + { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_FOUNTAIN_ANSI), + .driver_data = APPLE_NUMLOCK_EMULATION | APPLE_HAS_FN | + APPLE_IGNORE_MOUSE }, + { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_FOUNTAIN_ISO), + .driver_data = APPLE_NUMLOCK_EMULATION | APPLE_HAS_FN | + APPLE_IGNORE_MOUSE }, + { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_GEYSER_ANSI), + .driver_data = APPLE_NUMLOCK_EMULATION | APPLE_HAS_FN | + APPLE_IGNORE_MOUSE }, + { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_GEYSER_ISO), + .driver_data = APPLE_NUMLOCK_EMULATION | APPLE_HAS_FN | + APPLE_IGNORE_MOUSE | APPLE_ISO_KEYBOARD }, + { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_GEYSER_JIS), + .driver_data = APPLE_NUMLOCK_EMULATION | APPLE_HAS_FN | + APPLE_IGNORE_MOUSE }, + { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_GEYSER3_ANSI), + .driver_data = APPLE_NUMLOCK_EMULATION | APPLE_HAS_FN | + APPLE_IGNORE_MOUSE }, + { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_GEYSER3_ISO), + .driver_data = APPLE_NUMLOCK_EMULATION | APPLE_HAS_FN | + APPLE_IGNORE_MOUSE | APPLE_ISO_KEYBOARD }, + { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_GEYSER3_JIS), + .driver_data = APPLE_NUMLOCK_EMULATION | APPLE_HAS_FN | + APPLE_IGNORE_MOUSE | APPLE_RDESC_JIS }, + { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_GEYSER4_ANSI), + .driver_data = APPLE_NUMLOCK_EMULATION | APPLE_HAS_FN | + APPLE_IGNORE_MOUSE }, + { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_GEYSER4_ISO), + .driver_data = APPLE_NUMLOCK_EMULATION | APPLE_HAS_FN | + APPLE_IGNORE_MOUSE | APPLE_ISO_KEYBOARD }, + { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_GEYSER4_JIS), + .driver_data = APPLE_NUMLOCK_EMULATION | APPLE_HAS_FN | + APPLE_IGNORE_MOUSE | APPLE_RDESC_JIS}, + { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_ALU_ANSI), + .driver_data = APPLE_HAS_FN }, + { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_ALU_ISO), + .driver_data = APPLE_HAS_FN | APPLE_ISO_KEYBOARD }, + { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_ALU_JIS), + .driver_data = APPLE_HAS_FN }, + { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_GEYSER4_HF_ANSI), + .driver_data = APPLE_NUMLOCK_EMULATION | APPLE_HAS_FN | + APPLE_IGNORE_MOUSE }, + { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_GEYSER4_HF_ISO), + .driver_data = APPLE_NUMLOCK_EMULATION | APPLE_HAS_FN | + APPLE_IGNORE_MOUSE }, + { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_GEYSER4_HF_JIS), + .driver_data = APPLE_NUMLOCK_EMULATION | APPLE_HAS_FN | + APPLE_IGNORE_MOUSE | APPLE_RDESC_JIS }, + { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_ALU_WIRELESS_ANSI), + .driver_data = APPLE_NUMLOCK_EMULATION | APPLE_HAS_FN }, + { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_ALU_WIRELESS_ISO), + .driver_data = APPLE_NUMLOCK_EMULATION | APPLE_HAS_FN | + APPLE_ISO_KEYBOARD }, + { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_ALU_WIRELESS_JIS), + .driver_data = APPLE_NUMLOCK_EMULATION | APPLE_HAS_FN }, + { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_WELLSPRING_ANSI), + .driver_data = APPLE_HAS_FN | APPLE_IGNORE_MOUSE }, + { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_WELLSPRING_ISO), + .driver_data = APPLE_HAS_FN | APPLE_ISO_KEYBOARD | + APPLE_IGNORE_MOUSE }, + { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_WELLSPRING_JIS), + .driver_data = APPLE_HAS_FN | APPLE_IGNORE_MOUSE | APPLE_RDESC_JIS }, + { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_WELLSPRING2_ANSI), + .driver_data = APPLE_HAS_FN | APPLE_IGNORE_MOUSE }, + { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_WELLSPRING2_ISO), + .driver_data = APPLE_HAS_FN | APPLE_ISO_KEYBOARD | + APPLE_IGNORE_MOUSE }, + { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_WELLSPRING2_JIS), + .driver_data = APPLE_HAS_FN | APPLE_IGNORE_MOUSE | APPLE_RDESC_JIS }, + { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_FOUNTAIN_TP_ONLY), + .driver_data = APPLE_NUMLOCK_EMULATION | APPLE_HAS_FN | + APPLE_IGNORE_MOUSE }, + { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_GEYSER1_TP_ONLY), + .driver_data = APPLE_NUMLOCK_EMULATION | APPLE_HAS_FN | + APPLE_IGNORE_MOUSE }, + + /* Apple wireless Mighty Mouse */ + { HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_APPLE, 0x030c), + .driver_data = APPLE_MIGHTYMOUSE | APPLE_INVERT_HWHEEL }, + + { } +}; +MODULE_DEVICE_TABLE(hid, apple_devices); + +static struct hid_driver apple_driver = { + .name = "apple", + .id_table = apple_devices, + .report_fixup = apple_report_fixup, + .probe = apple_probe, + .remove = apple_remove, + .event = apple_event, + .input_mapping = apple_input_mapping, + .input_mapped = apple_input_mapped, +}; + +static int apple_init(void) +{ + int ret; + + ret = hid_register_driver(&apple_driver); + if (ret) + printk(KERN_ERR "can't register apple driver\n"); + + return ret; +} + +static void apple_exit(void) +{ + hid_unregister_driver(&apple_driver); +} + +module_init(apple_init); +module_exit(apple_exit); +MODULE_LICENSE("GPL"); + +HID_COMPAT_LOAD_DRIVER(apple); diff --git a/drivers/hid/hid-belkin.c b/drivers/hid/hid-belkin.c new file mode 100644 index 000000000000..12c8a9ba6ed6 --- /dev/null +++ b/drivers/hid/hid-belkin.c @@ -0,0 +1,105 @@ +/* + * HID driver for some belkin "special" devices + * + * 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) 2006-2007 Jiri Kosina + * Copyright (c) 2007 Paul Walmsley + * Copyright (c) 2008 Jiri Slaby + */ + +/* + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the Free + * Software Foundation; either version 2 of the License, or (at your option) + * any later version. + */ + +#include <linux/device.h> +#include <linux/hid.h> +#include <linux/module.h> + +#include "hid-ids.h" + +#define BELKIN_HIDDEV 0x01 +#define BELKIN_WKBD 0x02 + +#define belkin_map_key_clear(c) hid_map_usage_clear(hi, usage, bit, max, \ + EV_KEY, (c)) +static int belkin_input_mapping(struct hid_device *hdev, struct hid_input *hi, + struct hid_field *field, struct hid_usage *usage, + unsigned long **bit, int *max) +{ + unsigned long quirks = (unsigned long)hid_get_drvdata(hdev); + + if ((usage->hid & HID_USAGE_PAGE) != HID_UP_CONSUMER || + !(quirks & BELKIN_WKBD)) + return 0; + + switch (usage->hid & HID_USAGE) { + case 0x03a: belkin_map_key_clear(KEY_SOUND); break; + case 0x03b: belkin_map_key_clear(KEY_CAMERA); break; + case 0x03c: belkin_map_key_clear(KEY_DOCUMENTS); break; + default: + return 0; + } + return 1; +} + +static int belkin_probe(struct hid_device *hdev, const struct hid_device_id *id) +{ + unsigned long quirks = id->driver_data; + int ret; + + hid_set_drvdata(hdev, (void *)quirks); + + ret = hid_parse(hdev); + if (ret) { + dev_err(&hdev->dev, "parse failed\n"); + goto err_free; + } + + ret = hid_hw_start(hdev, HID_CONNECT_DEFAULT | + ((quirks & BELKIN_HIDDEV) ? HID_CONNECT_HIDDEV_FORCE : 0)); + if (ret) { + dev_err(&hdev->dev, "hw start failed\n"); + goto err_free; + } + + return 0; +err_free: + return ret; +} + +static const struct hid_device_id belkin_devices[] = { + { HID_USB_DEVICE(USB_VENDOR_ID_BELKIN, USB_DEVICE_ID_FLIP_KVM), + .driver_data = BELKIN_HIDDEV }, + { HID_USB_DEVICE(USB_VENDOR_ID_LABTEC, USB_DEVICE_ID_LABTEC_WIRELESS_KEYBOARD), + .driver_data = BELKIN_WKBD }, + { } +}; +MODULE_DEVICE_TABLE(hid, belkin_devices); + +static struct hid_driver belkin_driver = { + .name = "belkin", + .id_table = belkin_devices, + .input_mapping = belkin_input_mapping, + .probe = belkin_probe, +}; + +static int belkin_init(void) +{ + return hid_register_driver(&belkin_driver); +} + +static void belkin_exit(void) +{ + hid_unregister_driver(&belkin_driver); +} + +module_init(belkin_init); +module_exit(belkin_exit); +MODULE_LICENSE("GPL"); + +HID_COMPAT_LOAD_DRIVER(belkin); diff --git a/drivers/hid/hid-bright.c b/drivers/hid/hid-bright.c new file mode 100644 index 000000000000..38517a117dfd --- /dev/null +++ b/drivers/hid/hid-bright.c @@ -0,0 +1,71 @@ +/* + * HID driver for some bright "special" devices + * + * Copyright (c) 2008 Mauro Carvalho Chehab <mchehab@redhat.com> + * + * Based on hid-dell driver + */ + +/* + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the Free + * Software Foundation; either version 2 of the License, or (at your option) + * any later version. + */ + +#include <linux/device.h> +#include <linux/hid.h> +#include <linux/module.h> + +#include "hid-ids.h" + +static int bright_probe(struct hid_device *hdev, const struct hid_device_id *id) +{ + int ret; + + ret = hid_parse(hdev); + if (ret) { + dev_err(&hdev->dev, "parse failed\n"); + goto err_free; + } + + ret = hid_hw_start(hdev, HID_CONNECT_DEFAULT); + if (ret) { + dev_err(&hdev->dev, "hw start failed\n"); + goto err_free; + } + + usbhid_set_leds(hdev); + + return 0; +err_free: + return ret; +} + +static const struct hid_device_id bright_devices[] = { + { HID_USB_DEVICE(USB_VENDOR_ID_BRIGHT, USB_DEVICE_ID_BRIGHT_ABNT2) }, + { } +}; +MODULE_DEVICE_TABLE(hid, bright_devices); + +static struct hid_driver bright_driver = { + .name = "bright", + .id_table = bright_devices, + .probe = bright_probe, +}; + +static int bright_init(void) +{ + return hid_register_driver(&bright_driver); +} + +static void bright_exit(void) +{ + hid_unregister_driver(&bright_driver); +} + +module_init(bright_init); +module_exit(bright_exit); +MODULE_LICENSE("GPL"); + +HID_COMPAT_LOAD_DRIVER(bright); diff --git a/drivers/hid/hid-cherry.c b/drivers/hid/hid-cherry.c new file mode 100644 index 000000000000..b833b9769aba --- /dev/null +++ b/drivers/hid/hid-cherry.c @@ -0,0 +1,87 @@ +/* + * HID driver for some cherry "special" devices + * + * 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) 2006-2007 Jiri Kosina + * Copyright (c) 2007 Paul Walmsley + * Copyright (c) 2008 Jiri Slaby + */ + +/* + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the Free + * Software Foundation; either version 2 of the License, or (at your option) + * any later version. + */ + +#include <linux/device.h> +#include <linux/hid.h> +#include <linux/module.h> + +#include "hid-ids.h" + +/* + * Cherry Cymotion keyboard have an invalid HID report descriptor, + * that needs fixing before we can parse it. + */ +static void ch_report_fixup(struct hid_device *hdev, __u8 *rdesc, + unsigned int rsize) +{ + if (rsize >= 17 && rdesc[11] == 0x3c && rdesc[12] == 0x02) { + dev_info(&hdev->dev, "fixing up Cherry Cymotion report " + "descriptor\n"); + rdesc[11] = rdesc[16] = 0xff; + rdesc[12] = rdesc[17] = 0x03; + } +} + +#define ch_map_key_clear(c) hid_map_usage_clear(hi, usage, bit, max, \ + EV_KEY, (c)) +static int ch_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) != HID_UP_CONSUMER) + return 0; + + switch (usage->hid & HID_USAGE) { + case 0x301: ch_map_key_clear(KEY_PROG1); break; + case 0x302: ch_map_key_clear(KEY_PROG2); break; + case 0x303: ch_map_key_clear(KEY_PROG3); break; + default: + return 0; + } + + return 1; +} + +static const struct hid_device_id ch_devices[] = { + { HID_USB_DEVICE(USB_VENDOR_ID_CHERRY, USB_DEVICE_ID_CHERRY_CYMOTION) }, + { } +}; +MODULE_DEVICE_TABLE(hid, ch_devices); + +static struct hid_driver ch_driver = { + .name = "cherry", + .id_table = ch_devices, + .report_fixup = ch_report_fixup, + .input_mapping = ch_input_mapping, +}; + +static int ch_init(void) +{ + return hid_register_driver(&ch_driver); +} + +static void ch_exit(void) +{ + hid_unregister_driver(&ch_driver); +} + +module_init(ch_init); +module_exit(ch_exit); +MODULE_LICENSE("GPL"); + +HID_COMPAT_LOAD_DRIVER(cherry); diff --git a/drivers/hid/hid-chicony.c b/drivers/hid/hid-chicony.c new file mode 100644 index 000000000000..a54d4096e0f7 --- /dev/null +++ b/drivers/hid/hid-chicony.c @@ -0,0 +1,80 @@ +/* + * HID driver for some chicony "special" devices + * + * 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) 2006-2007 Jiri Kosina + * Copyright (c) 2007 Paul Walmsley + * Copyright (c) 2008 Jiri Slaby + */ + +/* + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the Free + * Software Foundation; either version 2 of the License, or (at your option) + * any later version. + */ + +#include <linux/device.h> +#include <linux/input.h> +#include <linux/hid.h> +#include <linux/module.h> + +#include "hid-ids.h" + +#define ch_map_key_clear(c) hid_map_usage_clear(hi, usage, bit, max, \ + EV_KEY, (c)) +static int ch_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) != HID_UP_MSVENDOR) + return 0; + + set_bit(EV_REP, hi->input->evbit); + switch (usage->hid & HID_USAGE) { + case 0xff01: ch_map_key_clear(BTN_1); break; + case 0xff02: ch_map_key_clear(BTN_2); break; + case 0xff03: ch_map_key_clear(BTN_3); break; + case 0xff04: ch_map_key_clear(BTN_4); break; + case 0xff05: ch_map_key_clear(BTN_5); break; + case 0xff06: ch_map_key_clear(BTN_6); break; + case 0xff07: ch_map_key_clear(BTN_7); break; + case 0xff08: ch_map_key_clear(BTN_8); break; + case 0xff09: ch_map_key_clear(BTN_9); break; + case 0xff0a: ch_map_key_clear(BTN_A); break; + case 0xff0b: ch_map_key_clear(BTN_B); break; + default: + return 0; + } + return 1; +} + +static const struct hid_device_id ch_devices[] = { + { HID_USB_DEVICE(USB_VENDOR_ID_CHICONY, USB_DEVICE_ID_CHICONY_TACTICAL_PAD) }, + { } +}; +MODULE_DEVICE_TABLE(hid, ch_devices); + +static struct hid_driver ch_driver = { + .name = "chicony", + .id_table = ch_devices, + .input_mapping = ch_input_mapping, +}; + +static int ch_init(void) +{ + return hid_register_driver(&ch_driver); +} + +static void ch_exit(void) +{ + hid_unregister_driver(&ch_driver); +} + +module_init(ch_init); +module_exit(ch_exit); +MODULE_LICENSE("GPL"); + +HID_COMPAT_LOAD_DRIVER(chicony); diff --git a/drivers/hid/hid-core.c b/drivers/hid/hid-core.c index 426ac5add585..8a7d9dbb4d07 100644 --- a/drivers/hid/hid-core.c +++ b/drivers/hid/hid-core.c @@ -33,6 +33,8 @@ #include <linux/hid-debug.h> #include <linux/hidraw.h> +#include "hid-ids.h" + /* * Version Information */ @@ -268,9 +270,9 @@ static int hid_add_field(struct hid_parser *parser, unsigned report_type, unsign static u32 item_udata(struct hid_item *item) { switch (item->size) { - case 1: return item->data.u8; - case 2: return item->data.u16; - case 4: return item->data.u32; + case 1: return item->data.u8; + case 2: return item->data.u16; + case 4: return item->data.u32; } return 0; } @@ -278,9 +280,9 @@ static u32 item_udata(struct hid_item *item) static s32 item_sdata(struct hid_item *item) { switch (item->size) { - case 1: return item->data.s8; - case 2: return item->data.s16; - case 4: return item->data.s32; + case 1: return item->data.s8; + case 2: return item->data.s16; + case 4: return item->data.s32; } return 0; } @@ -292,87 +294,91 @@ static s32 item_sdata(struct hid_item *item) static int hid_parser_global(struct hid_parser *parser, struct hid_item *item) { switch (item->tag) { + case HID_GLOBAL_ITEM_TAG_PUSH: - case HID_GLOBAL_ITEM_TAG_PUSH: - - if (parser->global_stack_ptr == HID_GLOBAL_STACK_SIZE) { - dbg_hid("global enviroment stack overflow\n"); - return -1; - } + if (parser->global_stack_ptr == HID_GLOBAL_STACK_SIZE) { + dbg_hid("global enviroment stack overflow\n"); + return -1; + } - memcpy(parser->global_stack + parser->global_stack_ptr++, - &parser->global, sizeof(struct hid_global)); - return 0; + memcpy(parser->global_stack + parser->global_stack_ptr++, + &parser->global, sizeof(struct hid_global)); + return 0; - case HID_GLOBAL_ITEM_TAG_POP: + case HID_GLOBAL_ITEM_TAG_POP: - if (!parser->global_stack_ptr) { - dbg_hid("global enviroment stack underflow\n"); - return -1; - } - - memcpy(&parser->global, parser->global_stack + --parser->global_stack_ptr, - sizeof(struct hid_global)); - return 0; + if (!parser->global_stack_ptr) { + dbg_hid("global enviroment stack underflow\n"); + return -1; + } - case HID_GLOBAL_ITEM_TAG_USAGE_PAGE: - parser->global.usage_page = item_udata(item); - return 0; + memcpy(&parser->global, parser->global_stack + + --parser->global_stack_ptr, sizeof(struct hid_global)); + return 0; - case HID_GLOBAL_ITEM_TAG_LOGICAL_MINIMUM: - parser->global.logical_minimum = item_sdata(item); - return 0; + case HID_GLOBAL_ITEM_TAG_USAGE_PAGE: + parser->global.usage_page = item_udata(item); + return 0; - case HID_GLOBAL_ITEM_TAG_LOGICAL_MAXIMUM: - if (parser->global.logical_minimum < 0) - parser->global.logical_maximum = item_sdata(item); - else - parser->global.logical_maximum = item_udata(item); - return 0; + case HID_GLOBAL_ITEM_TAG_LOGICAL_MINIMUM: + parser->global.logical_minimum = item_sdata(item); + return 0; - case HID_GLOBAL_ITEM_TAG_PHYSICAL_MINIMUM: - parser->global.physical_minimum = item_sdata(item); - return 0; + case HID_GLOBAL_ITEM_TAG_LOGICAL_MAXIMUM: + if (parser->global.logical_minimum < 0) + parser->global.logical_maximum = item_sdata(item); + else + parser->global.logical_maximum = item_udata(item); + return 0; - case HID_GLOBAL_ITEM_TAG_PHYSICAL_MAXIMUM: - if (parser->global.physical_minimum < 0) - parser->global.physical_maximum = item_sdata(item); - else - parser->global.physical_maximum = item_udata(item); - return 0; + case HID_GLOBAL_ITEM_TAG_PHYSICAL_MINIMUM: + parser->global.physical_minimum = item_sdata(item); + return 0; - case HID_GLOBAL_ITEM_TAG_UNIT_EXPONENT: - parser->global.unit_exponent = item_sdata(item); - return 0; + case HID_GLOBAL_ITEM_TAG_PHYSICAL_MAXIMUM: + if (parser->global.physical_minimum < 0) + parser->global.physical_maximum = item_sdata(item); + else + parser->global.physical_maximum = item_udata(item); + return 0; - case HID_GLOBAL_ITEM_TAG_UNIT: - parser->global.unit = item_udata(item); - return 0; + case HID_GLOBAL_ITEM_TAG_UNIT_EXPONENT: + parser->global.unit_exponent = item_sdata(item); + return 0; - case HID_GLOBAL_ITEM_TAG_REPORT_SIZE: - if ((parser->global.report_size = item_udata(item)) > 32) { - dbg_hid("invalid report_size %d\n", parser->global.report_size); - return -1; - } - return 0; + case HID_GLOBAL_ITEM_TAG_UNIT: + parser->global.unit = item_udata(item); + return 0; - case HID_GLOBAL_ITEM_TAG_REPORT_COUNT: - if ((parser->global.report_count = item_udata(item)) > HID_MAX_USAGES) { - dbg_hid("invalid report_count %d\n", parser->global.report_count); - return -1; - } - return 0; + case HID_GLOBAL_ITEM_TAG_REPORT_SIZE: + parser->global.report_size = item_udata(item); + if (parser->global.report_size > 32) { + dbg_hid("invalid report_size %d\n", + parser->global.report_size); + return -1; + } + return 0; - case HID_GLOBAL_ITEM_TAG_REPORT_ID: - if ((parser->global.report_id = item_udata(item)) == 0) { - dbg_hid("report_id 0 is invalid\n"); - return -1; - } - return 0; + case HID_GLOBAL_ITEM_TAG_REPORT_COUNT: + parser->global.report_count = item_udata(item); + if (parser->global.report_count > HID_MAX_USAGES) { + dbg_hid("invalid report_count %d\n", + parser->global.report_count); + return -1; + } + return 0; - default: - dbg_hid("unknown global tag 0x%x\n", item->tag); + case HID_GLOBAL_ITEM_TAG_REPORT_ID: + parser->global.report_id = item_udata(item); + if (parser->global.report_id == 0) { + dbg_hid("report_id 0 is invalid\n"); return -1; + } + return 0; + + default: + dbg_hid("unknown global tag 0x%x\n", item->tag); + return -1; } } @@ -393,77 +399,76 @@ static int hid_parser_local(struct hid_parser *parser, struct hid_item *item) data = item_udata(item); switch (item->tag) { - - case HID_LOCAL_ITEM_TAG_DELIMITER: - - if (data) { - /* - * We treat items before the first delimiter - * as global to all usage sets (branch 0). - * In the moment we process only these global - * items and the first delimiter set. - */ - if (parser->local.delimiter_depth != 0) { - dbg_hid("nested delimiters\n"); - return -1; - } - parser->local.delimiter_depth++; - parser->local.delimiter_branch++; - } else { - if (parser->local.delimiter_depth < 1) { - dbg_hid("bogus close delimiter\n"); - return -1; - } - parser->local.delimiter_depth--; + case HID_LOCAL_ITEM_TAG_DELIMITER: + + if (data) { + /* + * We treat items before the first delimiter + * as global to all usage sets (branch 0). + * In the moment we process only these global + * items and the first delimiter set. + */ + if (parser->local.delimiter_depth != 0) { + dbg_hid("nested delimiters\n"); + return -1; } - return 1; - - case HID_LOCAL_ITEM_TAG_USAGE: - - if (parser->local.delimiter_branch > 1) { - dbg_hid("alternative usage ignored\n"); - return 0; + parser->local.delimiter_depth++; + parser->local.delimiter_branch++; + } else { + if (parser->local.delimiter_depth < 1) { + dbg_hid("bogus close delimiter\n"); + return -1; } + parser->local.delimiter_depth--; + } + return 1; - if (item->size <= 2) - data = (parser->global.usage_page << 16) + data; + case HID_LOCAL_ITEM_TAG_USAGE: - return hid_add_usage(parser, data); + if (parser->local.delimiter_branch > 1) { + dbg_hid("alternative usage ignored\n"); + return 0; + } - case HID_LOCAL_ITEM_TAG_USAGE_MINIMUM: + if (item->size <= 2) + data = (parser->global.usage_page << 16) + data; - if (parser->local.delimiter_branch > 1) { - dbg_hid("alternative usage ignored\n"); - return 0; - } + return hid_add_usage(parser, data); - if (item->size <= 2) - data = (parser->global.usage_page << 16) + data; + case HID_LOCAL_ITEM_TAG_USAGE_MINIMUM: - parser->local.usage_minimum = data; + if (parser->local.delimiter_branch > 1) { + dbg_hid("alternative usage ignored\n"); return 0; + } - case HID_LOCAL_ITEM_TAG_USAGE_MAXIMUM: + if (item->size <= 2) + data = (parser->global.usage_page << 16) + data; - if (parser->local.delimiter_branch > 1) { - dbg_hid("alternative usage ignored\n"); - return 0; - } + parser->local.usage_minimum = data; + return 0; - if (item->size <= 2) - data = (parser->global.usage_page << 16) + data; + case HID_LOCAL_ITEM_TAG_USAGE_MAXIMUM: - for (n = parser->local.usage_minimum; n <= data; n++) - if (hid_add_usage(parser, n)) { - dbg_hid("hid_add_usage failed\n"); - return -1; - } + if (parser->local.delimiter_branch > 1) { + dbg_hid("alternative usage ignored\n"); return 0; + } - default: + if (item->size <= 2) + data = (parser->global.usage_page << 16) + data; - dbg_hid("unknown local item tag 0x%x\n", item->tag); - return 0; + for (n = parser->local.usage_minimum; n <= data; n++) + if (hid_add_usage(parser, n)) { + dbg_hid("hid_add_usage failed\n"); + return -1; + } + return 0; + + default: + + dbg_hid("unknown local item tag 0x%x\n", item->tag); + return 0; } return 0; } @@ -480,24 +485,24 @@ static int hid_parser_main(struct hid_parser *parser, struct hid_item *item) data = item_udata(item); switch (item->tag) { - case HID_MAIN_ITEM_TAG_BEGIN_COLLECTION: - ret = open_collection(parser, data & 0xff); - break; - case HID_MAIN_ITEM_TAG_END_COLLECTION: - ret = close_collection(parser); - break; - case HID_MAIN_ITEM_TAG_INPUT: - ret = hid_add_field(parser, HID_INPUT_REPORT, data); - break; - case HID_MAIN_ITEM_TAG_OUTPUT: - ret = hid_add_field(parser, HID_OUTPUT_REPORT, data); - break; - case HID_MAIN_ITEM_TAG_FEATURE: - ret = hid_add_field(parser, HID_FEATURE_REPORT, data); - break; - default: - dbg_hid("unknown main item tag 0x%x\n", item->tag); - ret = 0; + case HID_MAIN_ITEM_TAG_BEGIN_COLLECTION: + ret = open_collection(parser, data & 0xff); + break; + case HID_MAIN_ITEM_TAG_END_COLLECTION: + ret = close_collection(parser); + break; + case HID_MAIN_ITEM_TAG_INPUT: + ret = hid_add_field(parser, HID_INPUT_REPORT, data); + break; + case HID_MAIN_ITEM_TAG_OUTPUT: + ret = hid_add_field(parser, HID_OUTPUT_REPORT, data); + break; + case HID_MAIN_ITEM_TAG_FEATURE: + ret = hid_add_field(parser, HID_FEATURE_REPORT, data); + break; + default: + dbg_hid("unknown main item tag 0x%x\n", item->tag); + ret = 0; } memset(&parser->local, 0, sizeof(parser->local)); /* Reset the local parser environment */ @@ -534,9 +539,10 @@ static void hid_free_report(struct hid_report *report) * Free a device structure, all reports, and all fields. */ -void hid_free_device(struct hid_device *device) +static void hid_device_release(struct device *dev) { - unsigned i,j; + struct hid_device *device = container_of(dev, struct hid_device, dev); + unsigned i, j; for (i = 0; i < HID_REPORT_TYPES; i++) { struct hid_report_enum *report_enum = device->report_enum + i; @@ -552,7 +558,6 @@ void hid_free_device(struct hid_device *device) kfree(device->collection); kfree(device); } -EXPORT_SYMBOL_GPL(hid_free_device); /* * Fetch a report description item from the data stream. We support long @@ -593,47 +598,52 @@ static u8 *fetch_item(__u8 *start, __u8 *end, struct hid_item *item) item->size = b & 3; switch (item->size) { + case 0: + return start; - case 0: - return start; - - case 1: - if ((end - start) < 1) - return NULL; - item->data.u8 = *start++; - return start; + case 1: + if ((end - start) < 1) + return NULL; + item->data.u8 = *start++; + return start; - case 2: - if ((end - start) < 2) - return NULL; - item->data.u16 = get_unaligned_le16(start); - start = (__u8 *)((__le16 *)start + 1); - return start; + case 2: + if ((end - start) < 2) + return NULL; + item->data.u16 = get_unaligned_le16(start); + start = (__u8 *)((__le16 *)start + 1); + return start; - case 3: - item->size++; - if ((end - start) < 4) - return NULL; - item->data.u32 = get_unaligned_le32(start); - start = (__u8 *)((__le32 *)start + 1); - return start; + case 3: + item->size++; + if ((end - start) < 4) + return NULL; + item->data.u32 = get_unaligned_le32(start); + start = (__u8 *)((__le32 *)start + 1); + return start; } return NULL; } -/* +/** + * hid_parse_report - parse device report + * + * @device: hid device + * @start: report start + * @size: report size + * * Parse a report description into a hid_device structure. Reports are * enumerated, fields are attached to these reports. + * 0 returned on success, otherwise nonzero error value. */ - -struct hid_device *hid_parse_report(__u8 *start, unsigned size) +int hid_parse_report(struct hid_device *device, __u8 *start, + unsigned size) { - struct hid_device *device; struct hid_parser *parser; struct hid_item item; __u8 *end; - unsigned i; + int ret; static int (*dispatch_type[])(struct hid_parser *parser, struct hid_item *item) = { hid_parser_main, @@ -642,76 +652,57 @@ struct hid_device *hid_parse_report(__u8 *start, unsigned size) hid_parser_reserved }; - if (!(device = kzalloc(sizeof(struct hid_device), GFP_KERNEL))) - return NULL; + if (device->driver->report_fixup) + device->driver->report_fixup(device, start, size); - if (!(device->collection = kzalloc(sizeof(struct hid_collection) * - HID_DEFAULT_NUM_COLLECTIONS, GFP_KERNEL))) { - kfree(device); - return NULL; - } - device->collection_size = HID_DEFAULT_NUM_COLLECTIONS; - - for (i = 0; i < HID_REPORT_TYPES; i++) - INIT_LIST_HEAD(&device->report_enum[i].report_list); - - if (!(device->rdesc = kmalloc(size, GFP_KERNEL))) { - kfree(device->collection); - kfree(device); - return NULL; - } + device->rdesc = kmalloc(size, GFP_KERNEL); + if (device->rdesc == NULL) + return -ENOMEM; memcpy(device->rdesc, start, size); device->rsize = size; - if (!(parser = vmalloc(sizeof(struct hid_parser)))) { - kfree(device->rdesc); - kfree(device->collection); - kfree(device); - return NULL; + parser = vmalloc(sizeof(struct hid_parser)); + if (!parser) { + ret = -ENOMEM; + goto err; } + memset(parser, 0, sizeof(struct hid_parser)); parser->device = device; end = start + size; + ret = -EINVAL; while ((start = fetch_item(start, end, &item)) != NULL) { if (item.format != HID_ITEM_FORMAT_SHORT) { dbg_hid("unexpected long global item\n"); - hid_free_device(device); - vfree(parser); - return NULL; + goto err; } if (dispatch_type[item.type](parser, &item)) { dbg_hid("item %u %u %u %u parsing failed\n", item.format, (unsigned)item.size, (unsigned)item.type, (unsigned)item.tag); - hid_free_device(device); - vfree(parser); - return NULL; + goto err; } if (start == end) { if (parser->collection_stack_ptr) { dbg_hid("unbalanced collection at end of report description\n"); - hid_free_device(device); - vfree(parser); - return NULL; + goto err; } if (parser->local.delimiter_depth) { dbg_hid("unbalanced delimiter at end of report description\n"); - hid_free_device(device); - vfree(parser); - return NULL; + goto err; } vfree(parser); - return device; + return 0; } } dbg_hid("item fetching failed at offset %d\n", (int)(end - start)); - hid_free_device(device); +err: vfree(parser); - return NULL; + return ret; } EXPORT_SYMBOL_GPL(hid_parse_report); @@ -724,9 +715,9 @@ EXPORT_SYMBOL_GPL(hid_parse_report); static s32 snto32(__u32 value, unsigned n) { switch (n) { - case 8: return ((__s8)value); - case 16: return ((__s16)value); - case 32: return ((__s32)value); + case 8: return ((__s8)value); + case 16: return ((__s16)value); + case 32: return ((__s32)value); } return value & (1 << (n - 1)) ? value | (-1 << n) : value; } @@ -815,9 +806,73 @@ static __inline__ int search(__s32 *array, __s32 value, unsigned n) return -1; } -static void hid_process_event(struct hid_device *hid, struct hid_field *field, struct hid_usage *usage, __s32 value, int interrupt) +/** + * hid_match_report - check if driver's raw_event should be called + * + * @hid: hid device + * @report_type: type to match against + * + * compare hid->driver->report_table->report_type to report->type + */ +static int hid_match_report(struct hid_device *hid, struct hid_report *report) +{ + const struct hid_report_id *id = hid->driver->report_table; + + if (!id) /* NULL means all */ + return 1; + + for (; id->report_type != HID_TERMINATOR; id++) + if (id->report_type == HID_ANY_ID || + id->report_type == report->type) + return 1; + return 0; +} + +/** + * hid_match_usage - check if driver's event should be called + * + * @hid: hid device + * @usage: usage to match against + * + * compare hid->driver->usage_table->usage_{type,code} to + * usage->usage_{type,code} + */ +static int hid_match_usage(struct hid_device *hid, struct hid_usage *usage) { + const struct hid_usage_id *id = hid->driver->usage_table; + + if (!id) /* NULL means all */ + return 1; + + for (; id->usage_type != HID_ANY_ID - 1; id++) + if ((id->usage_hid == HID_ANY_ID || + id->usage_hid == usage->hid) && + (id->usage_type == HID_ANY_ID || + id->usage_type == usage->type) && + (id->usage_code == HID_ANY_ID || + id->usage_code == usage->code)) + return 1; + return 0; +} + +static void hid_process_event(struct hid_device *hid, struct hid_field *field, + struct hid_usage *usage, __s32 value, int interrupt) +{ + struct hid_driver *hdrv = hid->driver; + int ret; + hid_dump_input(usage, value); + + if (hdrv && hdrv->event && hid_match_usage(hid, usage)) { + ret = hdrv->event(hid, field, usage, value); + if (ret != 0) { + if (ret < 0) + dbg_hid("%s's event failed with %d\n", + hdrv->name, ret); + return; + } + } + if (hid->claimed & HID_CLAIMED_INPUT) hidinput_hid_event(hid, field, usage, value); if (hid->claimed & HID_CLAIMED_HIDDEV && interrupt && hid->hiddev_hid_event) @@ -946,44 +1001,47 @@ int hid_set_field(struct hid_field *field, unsigned offset, __s32 value) } EXPORT_SYMBOL_GPL(hid_set_field); -int hid_input_report(struct hid_device *hid, int type, u8 *data, int size, int interrupt) +static struct hid_report *hid_get_report(struct hid_report_enum *report_enum, + const u8 *data) { - struct hid_report_enum *report_enum = hid->report_enum + type; struct hid_report *report; - int n, rsize, i; + unsigned int n = 0; /* Normally report number is 0 */ - if (!hid) - return -ENODEV; + /* Device uses numbered reports, data[0] is report number */ + if (report_enum->numbered) + n = *data; - if (!size) { - dbg_hid("empty report\n"); - return -1; - } + report = report_enum->report_id_hash[n]; + if (report == NULL) + dbg_hid("undefined report_id %u received\n", n); - dbg_hid("report (size %u) (%snumbered)\n", size, report_enum->numbered ? "" : "un"); + return report; +} - n = 0; /* Normally report number is 0 */ - if (report_enum->numbered) { /* Device uses numbered reports, data[0] is report number */ - n = *data++; - size--; - } +void hid_report_raw_event(struct hid_device *hid, int type, u8 *data, int size, + int interrupt) +{ + struct hid_report_enum *report_enum = hid->report_enum + type; + struct hid_report *report; + unsigned int a; + int rsize, csize = size; + u8 *cdata = data; - /* dump the report */ - dbg_hid("report %d (size %u) = ", n, size); - for (i = 0; i < size; i++) - dbg_hid_line(" %02x", data[i]); - dbg_hid_line("\n"); + report = hid_get_report(report_enum, data); + if (!report) + return; - if (!(report = report_enum->report_id_hash[n])) { - dbg_hid("undefined report_id %d received\n", n); - return -1; + if (report_enum->numbered) { + cdata++; + csize--; } rsize = ((report->size - 1) >> 3) + 1; - if (size < rsize) { - dbg_hid("report %d is too short, (%d < %d)\n", report->id, size, rsize); - memset(data + size, 0, rsize - size); + if (csize < rsize) { + dbg_hid("report %d is too short, (%d < %d)\n", report->id, + csize, rsize); + memset(cdata + csize, 0, rsize - csize); } if ((hid->claimed & HID_CLAIMED_HIDDEV) && hid->hiddev_report_event) @@ -996,24 +1054,661 @@ int hid_input_report(struct hid_device *hid, int type, u8 *data, int size, int i hidraw_report_event(hid, data, size); } - for (n = 0; n < report->maxfield; n++) - hid_input_field(hid, report->field[n], data, interrupt); + for (a = 0; a < report->maxfield; a++) + hid_input_field(hid, report->field[a], cdata, interrupt); if (hid->claimed & HID_CLAIMED_INPUT) hidinput_report_event(hid, report); +} +EXPORT_SYMBOL_GPL(hid_report_raw_event); + +/** + * hid_input_report - report data from lower layer (usb, bt...) + * + * @hid: hid device + * @type: HID report type (HID_*_REPORT) + * @data: report contents + * @size: size of data parameter + * @interrupt: called from atomic? + * + * This is data entry for lower layers. + */ +int hid_input_report(struct hid_device *hid, int type, u8 *data, int size, int interrupt) +{ + struct hid_report_enum *report_enum = hid->report_enum + type; + struct hid_driver *hdrv = hid->driver; + struct hid_report *report; + unsigned int i; + int ret; + + if (!hid || !hid->driver) + return -ENODEV; + + if (!size) { + dbg_hid("empty report\n"); + return -1; + } + + dbg_hid("report (size %u) (%snumbered)\n", size, report_enum->numbered ? "" : "un"); + + report = hid_get_report(report_enum, data); + if (!report) + return -1; + + /* dump the report */ + dbg_hid("report %d (size %u) = ", report->id, size); + for (i = 0; i < size; i++) + dbg_hid_line(" %02x", data[i]); + dbg_hid_line("\n"); + + if (hdrv && hdrv->raw_event && hid_match_report(hid, report)) { + ret = hdrv->raw_event(hid, report, data, size); + if (ret != 0) + return ret < 0 ? ret : 0; + } + + hid_report_raw_event(hid, type, data, size, interrupt); return 0; } EXPORT_SYMBOL_GPL(hid_input_report); +static bool hid_match_one_id(struct hid_device *hdev, + const struct hid_device_id *id) +{ + return id->bus == hdev->bus && + (id->vendor == HID_ANY_ID || id->vendor == hdev->vendor) && + (id->product == HID_ANY_ID || id->product == hdev->product); +} + +static const struct hid_device_id *hid_match_id(struct hid_device *hdev, + const struct hid_device_id *id) +{ + for (; id->bus; id++) + if (hid_match_one_id(hdev, id)) + return id; + + return NULL; +} + +static const struct hid_device_id hid_hiddev_list[] = { + { HID_USB_DEVICE(USB_VENDOR_ID_MGE, USB_DEVICE_ID_MGE_UPS) }, + { HID_USB_DEVICE(USB_VENDOR_ID_MGE, USB_DEVICE_ID_MGE_UPS1) }, + { } +}; + +static bool hid_hiddev(struct hid_device *hdev) +{ + return !!hid_match_id(hdev, hid_hiddev_list); +} + +int hid_connect(struct hid_device *hdev, unsigned int connect_mask) +{ + static const char *types[] = { "Device", "Pointer", "Mouse", "Device", + "Joystick", "Gamepad", "Keyboard", "Keypad", + "Multi-Axis Controller" + }; + const char *type, *bus; + char buf[64]; + unsigned int i; + int len; + + if (hdev->bus != BUS_USB) + connect_mask &= ~HID_CONNECT_HIDDEV; + if (hid_hiddev(hdev)) + connect_mask |= HID_CONNECT_HIDDEV_FORCE; + + if ((connect_mask & HID_CONNECT_HIDINPUT) && !hidinput_connect(hdev, + connect_mask & HID_CONNECT_HIDINPUT_FORCE)) + hdev->claimed |= HID_CLAIMED_INPUT; + if ((connect_mask & HID_CONNECT_HIDDEV) && hdev->hiddev_connect && + !hdev->hiddev_connect(hdev, + connect_mask & HID_CONNECT_HIDDEV_FORCE)) + hdev->claimed |= HID_CLAIMED_HIDDEV; + if ((connect_mask & HID_CONNECT_HIDRAW) && !hidraw_connect(hdev)) + hdev->claimed |= HID_CLAIMED_HIDRAW; + + if (!hdev->claimed) { + dev_err(&hdev->dev, "claimed by neither input, hiddev nor " + "hidraw\n"); + return -ENODEV; + } + + if ((hdev->claimed & HID_CLAIMED_INPUT) && + (connect_mask & HID_CONNECT_FF) && hdev->ff_init) + hdev->ff_init(hdev); + + len = 0; + if (hdev->claimed & HID_CLAIMED_INPUT) + len += sprintf(buf + len, "input"); + if (hdev->claimed & HID_CLAIMED_HIDDEV) + len += sprintf(buf + len, "%shiddev%d", len ? "," : "", + hdev->minor); + if (hdev->claimed & HID_CLAIMED_HIDRAW) + len += sprintf(buf + len, "%shidraw%d", len ? "," : "", + ((struct hidraw *)hdev->hidraw)->minor); + + type = "Device"; + for (i = 0; i < hdev->maxcollection; i++) { + struct hid_collection *col = &hdev->collection[i]; + if (col->type == HID_COLLECTION_APPLICATION && + (col->usage & HID_USAGE_PAGE) == HID_UP_GENDESK && + (col->usage & 0xffff) < ARRAY_SIZE(types)) { + type = types[col->usage & 0xffff]; + break; + } + } + + switch (hdev->bus) { + case BUS_USB: + bus = "USB"; + break; + case BUS_BLUETOOTH: + bus = "BLUETOOTH"; + break; + default: + bus = "<UNKNOWN>"; + } + + dev_info(&hdev->dev, "%s: %s HID v%x.%02x %s [%s] on %s\n", + buf, bus, hdev->version >> 8, hdev->version & 0xff, + type, hdev->name, hdev->phys); + + return 0; +} +EXPORT_SYMBOL_GPL(hid_connect); + +static const struct hid_device_id hid_blacklist[] = { + { 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) }, + { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_ATV_IRCONTROL) }, + { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_IRCONTROL4) }, + { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_MIGHTYMOUSE) }, + { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_FOUNTAIN_ANSI) }, + { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_FOUNTAIN_ISO) }, + { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_GEYSER_ANSI) }, + { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_GEYSER_ISO) }, + { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_GEYSER_JIS) }, + { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_GEYSER3_ANSI) }, + { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_GEYSER3_ISO) }, + { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_GEYSER3_JIS) }, + { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_GEYSER4_ANSI) }, + { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_GEYSER4_ISO) }, + { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_GEYSER4_JIS) }, + { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_ALU_ANSI) }, + { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_ALU_ISO) }, + { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_ALU_JIS) }, + { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_GEYSER4_HF_ANSI) }, + { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_GEYSER4_HF_ISO) }, + { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_GEYSER4_HF_JIS) }, + { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_ALU_WIRELESS_ANSI) }, + { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_ALU_WIRELESS_ISO) }, + { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_ALU_WIRELESS_JIS) }, + { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_WELLSPRING_ANSI) }, + { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_WELLSPRING_ISO) }, + { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_WELLSPRING_JIS) }, + { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_WELLSPRING2_ANSI) }, + { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_WELLSPRING2_ISO) }, + { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_WELLSPRING2_JIS) }, + { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_FOUNTAIN_TP_ONLY) }, + { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_GEYSER1_TP_ONLY) }, + { HID_USB_DEVICE(USB_VENDOR_ID_AVERMEDIA, USB_DEVICE_ID_AVER_FM_MR800) }, + { HID_USB_DEVICE(USB_VENDOR_ID_BELKIN, USB_DEVICE_ID_FLIP_KVM) }, + { HID_USB_DEVICE(USB_VENDOR_ID_BRIGHT, USB_DEVICE_ID_BRIGHT_ABNT2) }, + { HID_USB_DEVICE(USB_VENDOR_ID_CHERRY, USB_DEVICE_ID_CHERRY_CYMOTION) }, + { HID_USB_DEVICE(USB_VENDOR_ID_CHICONY, USB_DEVICE_ID_CHICONY_TACTICAL_PAD) }, + { HID_USB_DEVICE(USB_VENDOR_ID_CYPRESS, USB_DEVICE_ID_CYPRESS_BARCODE_1) }, + { HID_USB_DEVICE(USB_VENDOR_ID_CYPRESS, USB_DEVICE_ID_CYPRESS_BARCODE_2) }, + { HID_USB_DEVICE(USB_VENDOR_ID_CYPRESS, USB_DEVICE_ID_CYPRESS_MOUSE) }, + { HID_USB_DEVICE(USB_VENDOR_ID_DELL, USB_DEVICE_ID_DELL_W7658) }, + { HID_USB_DEVICE(USB_VENDOR_ID_DELL, USB_DEVICE_ID_DELL_SK8115) }, + { HID_USB_DEVICE(USB_VENDOR_ID_EZKEY, USB_DEVICE_ID_BTC_8193) }, + { HID_USB_DEVICE(USB_VENDOR_ID_GYRATION, USB_DEVICE_ID_GYRATION_REMOTE) }, + { HID_USB_DEVICE(USB_VENDOR_ID_LABTEC, USB_DEVICE_ID_LABTEC_WIRELESS_KEYBOARD) }, + { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_MX3000_RECEIVER) }, + { 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_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) }, + { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_KBD) }, + { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_ELITE_KBD) }, + { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_CORDLESS_DESKTOP_LX500) }, + { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_LX3) }, + { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_V150) }, + { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_EXTREME_3D) }, + { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_WHEEL) }, + { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_RUMBLEPAD) }, + { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_RUMBLEPAD2_2) }, + { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_WINGMAN_F3D) }, + { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_FORCE3D_PRO) }, + { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_MOMO_WHEEL) }, + { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_MOMO_WHEEL2) }, + { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_RUMBLEPAD2) }, + { HID_USB_DEVICE(USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_SIDEWINDER_GV) }, + { HID_USB_DEVICE(USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_MS_NE4K) }, + { HID_USB_DEVICE(USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_MS_LK6K) }, + { HID_USB_DEVICE(USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_MS_PRESENTER_8K_USB) }, + { HID_USB_DEVICE(USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_WIRELESS_OPTICAL_DESKTOP_3_0) }, + { HID_USB_DEVICE(USB_VENDOR_ID_MONTEREY, USB_DEVICE_ID_GENIUS_KB29E) }, + { HID_USB_DEVICE(USB_VENDOR_ID_PETALYNX, USB_DEVICE_ID_PETALYNX_MAXTER_REMOTE) }, + { HID_USB_DEVICE(USB_VENDOR_ID_SAMSUNG, USB_DEVICE_ID_SAMSUNG_IR_REMOTE) }, + { HID_USB_DEVICE(USB_VENDOR_ID_SONY, USB_DEVICE_ID_SONY_PS3_CONTROLLER) }, + { HID_USB_DEVICE(USB_VENDOR_ID_SUNPLUS, USB_DEVICE_ID_SUNPLUS_WDESKTOP) }, + + { HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_APPLE, 0x030c) }, + { HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_MS_PRESENTER_8K_BT) }, + { } +}; + +static int hid_bus_match(struct device *dev, struct device_driver *drv) +{ + struct hid_driver *hdrv = container_of(drv, struct hid_driver, driver); + struct hid_device *hdev = container_of(dev, struct hid_device, dev); + + if (!hid_match_id(hdev, hdrv->id_table)) + return 0; + + /* generic wants all non-blacklisted */ + if (!strncmp(hdrv->name, "generic-", 8)) + return !hid_match_id(hdev, hid_blacklist); + + return 1; +} + +static int hid_device_probe(struct device *dev) +{ + struct hid_driver *hdrv = container_of(dev->driver, + struct hid_driver, driver); + struct hid_device *hdev = container_of(dev, struct hid_device, dev); + const struct hid_device_id *id; + int ret = 0; + + if (!hdev->driver) { + id = hid_match_id(hdev, hdrv->id_table); + if (id == NULL) + return -ENODEV; + + hdev->driver = hdrv; + if (hdrv->probe) { + ret = hdrv->probe(hdev, id); + } else { /* default probe */ + ret = hid_parse(hdev); + if (!ret) + ret = hid_hw_start(hdev, HID_CONNECT_DEFAULT); + } + if (ret) + hdev->driver = NULL; + } + return ret; +} + +static int hid_device_remove(struct device *dev) +{ + struct hid_device *hdev = container_of(dev, struct hid_device, dev); + struct hid_driver *hdrv = hdev->driver; + + if (hdrv) { + if (hdrv->remove) + hdrv->remove(hdev); + else /* default remove */ + hid_hw_stop(hdev); + hdev->driver = NULL; + } + + return 0; +} + +static int hid_uevent(struct device *dev, struct kobj_uevent_env *env) +{ + struct hid_device *hdev = container_of(dev, struct hid_device, dev); + + if (add_uevent_var(env, "HID_ID=%04X:%08X:%08X", + hdev->bus, hdev->vendor, hdev->product)) + return -ENOMEM; + + if (add_uevent_var(env, "HID_NAME=%s", hdev->name)) + return -ENOMEM; + + if (add_uevent_var(env, "HID_PHYS=%s", hdev->phys)) + return -ENOMEM; + + if (add_uevent_var(env, "HID_UNIQ=%s", hdev->uniq)) + return -ENOMEM; + + if (add_uevent_var(env, "MODALIAS=hid:b%04Xv%08Xp%08X", + hdev->bus, hdev->vendor, hdev->product)) + return -ENOMEM; + + return 0; +} + +static struct bus_type hid_bus_type = { + .name = "hid", + .match = hid_bus_match, + .probe = hid_device_probe, + .remove = hid_device_remove, + .uevent = hid_uevent, +}; + +static const struct hid_device_id hid_ignore_list[] = { + { HID_USB_DEVICE(USB_VENDOR_ID_ACECAD, USB_DEVICE_ID_ACECAD_FLAIR) }, + { HID_USB_DEVICE(USB_VENDOR_ID_ACECAD, USB_DEVICE_ID_ACECAD_302) }, + { HID_USB_DEVICE(USB_VENDOR_ID_ADS_TECH, USB_DEVICE_ID_ADS_TECH_RADIO_SI470X) }, + { HID_USB_DEVICE(USB_VENDOR_ID_AIPTEK, USB_DEVICE_ID_AIPTEK_01) }, + { HID_USB_DEVICE(USB_VENDOR_ID_AIPTEK, USB_DEVICE_ID_AIPTEK_10) }, + { HID_USB_DEVICE(USB_VENDOR_ID_AIPTEK, USB_DEVICE_ID_AIPTEK_20) }, + { HID_USB_DEVICE(USB_VENDOR_ID_AIPTEK, USB_DEVICE_ID_AIPTEK_21) }, + { HID_USB_DEVICE(USB_VENDOR_ID_AIPTEK, USB_DEVICE_ID_AIPTEK_22) }, + { HID_USB_DEVICE(USB_VENDOR_ID_AIPTEK, USB_DEVICE_ID_AIPTEK_23) }, + { HID_USB_DEVICE(USB_VENDOR_ID_AIPTEK, USB_DEVICE_ID_AIPTEK_24) }, + { HID_USB_DEVICE(USB_VENDOR_ID_AIRCABLE, USB_DEVICE_ID_AIRCABLE1) }, + { HID_USB_DEVICE(USB_VENDOR_ID_ALCOR, USB_DEVICE_ID_ALCOR_USBRS232) }, + { HID_USB_DEVICE(USB_VENDOR_ID_ASUS, USB_DEVICE_ID_ASUS_LCM)}, + { HID_USB_DEVICE(USB_VENDOR_ID_BERKSHIRE, USB_DEVICE_ID_BERKSHIRE_PCWD) }, + { HID_USB_DEVICE(USB_VENDOR_ID_CIDC, 0x0103) }, + { HID_USB_DEVICE(USB_VENDOR_ID_CYGNAL, USB_DEVICE_ID_CYGNAL_RADIO_SI470X) }, + { HID_USB_DEVICE(USB_VENDOR_ID_CMEDIA, USB_DEVICE_ID_CM109) }, + { HID_USB_DEVICE(USB_VENDOR_ID_CYPRESS, USB_DEVICE_ID_CYPRESS_HIDCOM) }, + { HID_USB_DEVICE(USB_VENDOR_ID_CYPRESS, USB_DEVICE_ID_CYPRESS_ULTRAMOUSE) }, + { HID_USB_DEVICE(USB_VENDOR_ID_DELORME, USB_DEVICE_ID_DELORME_EARTHMATE) }, + { HID_USB_DEVICE(USB_VENDOR_ID_DELORME, USB_DEVICE_ID_DELORME_EM_LT20) }, + { HID_USB_DEVICE(USB_VENDOR_ID_ESSENTIAL_REALITY, USB_DEVICE_ID_ESSENTIAL_REALITY_P5) }, + { HID_USB_DEVICE(USB_VENDOR_ID_GAMERON, USB_DEVICE_ID_GAMERON_DUAL_PSX_ADAPTOR) }, + { HID_USB_DEVICE(USB_VENDOR_ID_GENERAL_TOUCH, 0x0001) }, + { HID_USB_DEVICE(USB_VENDOR_ID_GENERAL_TOUCH, 0x0002) }, + { HID_USB_DEVICE(USB_VENDOR_ID_GENERAL_TOUCH, 0x0003) }, + { HID_USB_DEVICE(USB_VENDOR_ID_GENERAL_TOUCH, 0x0004) }, + { HID_USB_DEVICE(USB_VENDOR_ID_GLAB, USB_DEVICE_ID_4_PHIDGETSERVO_30) }, + { HID_USB_DEVICE(USB_VENDOR_ID_GLAB, USB_DEVICE_ID_1_PHIDGETSERVO_30) }, + { HID_USB_DEVICE(USB_VENDOR_ID_GLAB, USB_DEVICE_ID_0_0_4_IF_KIT) }, + { HID_USB_DEVICE(USB_VENDOR_ID_GLAB, USB_DEVICE_ID_0_16_16_IF_KIT) }, + { HID_USB_DEVICE(USB_VENDOR_ID_GLAB, USB_DEVICE_ID_8_8_8_IF_KIT) }, + { HID_USB_DEVICE(USB_VENDOR_ID_GLAB, USB_DEVICE_ID_0_8_7_IF_KIT) }, + { HID_USB_DEVICE(USB_VENDOR_ID_GLAB, USB_DEVICE_ID_0_8_8_IF_KIT) }, + { HID_USB_DEVICE(USB_VENDOR_ID_GLAB, USB_DEVICE_ID_PHIDGET_MOTORCONTROL) }, + { HID_USB_DEVICE(USB_VENDOR_ID_GOTOP, USB_DEVICE_ID_SUPER_Q2) }, + { HID_USB_DEVICE(USB_VENDOR_ID_GOTOP, USB_DEVICE_ID_GOGOPEN) }, + { HID_USB_DEVICE(USB_VENDOR_ID_GOTOP, USB_DEVICE_ID_PENPOWER) }, + { HID_USB_DEVICE(USB_VENDOR_ID_GREENASIA, 0x0003) }, + { HID_USB_DEVICE(USB_VENDOR_ID_GRETAGMACBETH, USB_DEVICE_ID_GRETAGMACBETH_HUEY) }, + { HID_USB_DEVICE(USB_VENDOR_ID_GRIFFIN, USB_DEVICE_ID_POWERMATE) }, + { HID_USB_DEVICE(USB_VENDOR_ID_GRIFFIN, USB_DEVICE_ID_SOUNDKNOB) }, + { HID_USB_DEVICE(USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_90) }, + { HID_USB_DEVICE(USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_100) }, + { HID_USB_DEVICE(USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_101) }, + { HID_USB_DEVICE(USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_103) }, + { HID_USB_DEVICE(USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_104) }, + { HID_USB_DEVICE(USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_105) }, + { HID_USB_DEVICE(USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_106) }, + { HID_USB_DEVICE(USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_107) }, + { HID_USB_DEVICE(USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_108) }, + { HID_USB_DEVICE(USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_200) }, + { HID_USB_DEVICE(USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_201) }, + { HID_USB_DEVICE(USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_202) }, + { HID_USB_DEVICE(USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_203) }, + { HID_USB_DEVICE(USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_204) }, + { HID_USB_DEVICE(USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_205) }, + { HID_USB_DEVICE(USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_206) }, + { HID_USB_DEVICE(USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_207) }, + { HID_USB_DEVICE(USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_300) }, + { HID_USB_DEVICE(USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_301) }, + { HID_USB_DEVICE(USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_302) }, + { HID_USB_DEVICE(USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_303) }, + { HID_USB_DEVICE(USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_304) }, + { HID_USB_DEVICE(USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_305) }, + { HID_USB_DEVICE(USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_306) }, + { HID_USB_DEVICE(USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_307) }, + { HID_USB_DEVICE(USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_308) }, + { HID_USB_DEVICE(USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_309) }, + { HID_USB_DEVICE(USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_400) }, + { HID_USB_DEVICE(USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_401) }, + { HID_USB_DEVICE(USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_402) }, + { HID_USB_DEVICE(USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_403) }, + { HID_USB_DEVICE(USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_404) }, + { HID_USB_DEVICE(USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_405) }, + { HID_USB_DEVICE(USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_500) }, + { HID_USB_DEVICE(USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_501) }, + { HID_USB_DEVICE(USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_502) }, + { HID_USB_DEVICE(USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_503) }, + { HID_USB_DEVICE(USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_504) }, + { HID_USB_DEVICE(USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_1000) }, + { HID_USB_DEVICE(USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_1001) }, + { HID_USB_DEVICE(USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_1002) }, + { HID_USB_DEVICE(USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_1003) }, + { HID_USB_DEVICE(USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_1004) }, + { HID_USB_DEVICE(USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_1005) }, + { HID_USB_DEVICE(USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_1006) }, + { HID_USB_DEVICE(USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_1007) }, + { HID_USB_DEVICE(USB_VENDOR_ID_IMATION, USB_DEVICE_ID_DISC_STAKKA) }, + { HID_USB_DEVICE(USB_VENDOR_ID_KBGEAR, USB_DEVICE_ID_KBGEAR_JAMSTUDIO) }, + { HID_USB_DEVICE(USB_VENDOR_ID_KYE, USB_DEVICE_ID_KYE_GPEN_560) }, + { HID_USB_DEVICE(USB_VENDOR_ID_LD, USB_DEVICE_ID_LD_CASSY) }, + { HID_USB_DEVICE(USB_VENDOR_ID_LD, USB_DEVICE_ID_LD_POCKETCASSY) }, + { HID_USB_DEVICE(USB_VENDOR_ID_LD, USB_DEVICE_ID_LD_MOBILECASSY) }, + { HID_USB_DEVICE(USB_VENDOR_ID_LD, USB_DEVICE_ID_LD_JWM) }, + { HID_USB_DEVICE(USB_VENDOR_ID_LD, USB_DEVICE_ID_LD_DMMP) }, + { HID_USB_DEVICE(USB_VENDOR_ID_LD, USB_DEVICE_ID_LD_UMIP) }, + { HID_USB_DEVICE(USB_VENDOR_ID_LD, USB_DEVICE_ID_LD_XRAY1) }, + { HID_USB_DEVICE(USB_VENDOR_ID_LD, USB_DEVICE_ID_LD_XRAY2) }, + { HID_USB_DEVICE(USB_VENDOR_ID_LD, USB_DEVICE_ID_LD_VIDEOCOM) }, + { HID_USB_DEVICE(USB_VENDOR_ID_LD, USB_DEVICE_ID_LD_COM3LAB) }, + { HID_USB_DEVICE(USB_VENDOR_ID_LD, USB_DEVICE_ID_LD_TELEPORT) }, + { HID_USB_DEVICE(USB_VENDOR_ID_LD, USB_DEVICE_ID_LD_NETWORKANALYSER) }, + { HID_USB_DEVICE(USB_VENDOR_ID_LD, USB_DEVICE_ID_LD_POWERCONTROL) }, + { HID_USB_DEVICE(USB_VENDOR_ID_LD, USB_DEVICE_ID_LD_MACHINETEST) }, + { HID_USB_DEVICE(USB_VENDOR_ID_MCC, USB_DEVICE_ID_MCC_PMD1024LS) }, + { HID_USB_DEVICE(USB_VENDOR_ID_MCC, USB_DEVICE_ID_MCC_PMD1208LS) }, + { HID_USB_DEVICE(USB_VENDOR_ID_MICROCHIP, USB_DEVICE_ID_PICKIT1) }, + { HID_USB_DEVICE(USB_VENDOR_ID_MICROCHIP, USB_DEVICE_ID_PICKIT2) }, + { HID_USB_DEVICE(USB_VENDOR_ID_NATIONAL_SEMICONDUCTOR, USB_DEVICE_ID_N_S_HARMONY) }, + { HID_USB_DEVICE(USB_VENDOR_ID_ONTRAK, USB_DEVICE_ID_ONTRAK_ADU100) }, + { HID_USB_DEVICE(USB_VENDOR_ID_ONTRAK, USB_DEVICE_ID_ONTRAK_ADU100 + 20) }, + { HID_USB_DEVICE(USB_VENDOR_ID_ONTRAK, USB_DEVICE_ID_ONTRAK_ADU100 + 30) }, + { HID_USB_DEVICE(USB_VENDOR_ID_ONTRAK, USB_DEVICE_ID_ONTRAK_ADU100 + 100) }, + { HID_USB_DEVICE(USB_VENDOR_ID_ONTRAK, USB_DEVICE_ID_ONTRAK_ADU100 + 108) }, + { HID_USB_DEVICE(USB_VENDOR_ID_ONTRAK, USB_DEVICE_ID_ONTRAK_ADU100 + 118) }, + { HID_USB_DEVICE(USB_VENDOR_ID_ONTRAK, USB_DEVICE_ID_ONTRAK_ADU100 + 200) }, + { HID_USB_DEVICE(USB_VENDOR_ID_ONTRAK, USB_DEVICE_ID_ONTRAK_ADU100 + 300) }, + { HID_USB_DEVICE(USB_VENDOR_ID_ONTRAK, USB_DEVICE_ID_ONTRAK_ADU100 + 400) }, + { HID_USB_DEVICE(USB_VENDOR_ID_ONTRAK, USB_DEVICE_ID_ONTRAK_ADU100 + 500) }, + { HID_USB_DEVICE(USB_VENDOR_ID_PANJIT, 0x0001) }, + { HID_USB_DEVICE(USB_VENDOR_ID_PANJIT, 0x0002) }, + { HID_USB_DEVICE(USB_VENDOR_ID_PANJIT, 0x0003) }, + { HID_USB_DEVICE(USB_VENDOR_ID_PANJIT, 0x0004) }, + { HID_USB_DEVICE(USB_VENDOR_ID_SOUNDGRAPH, USB_DEVICE_ID_SOUNDGRAPH_IMON_LCD) }, + { HID_USB_DEVICE(USB_VENDOR_ID_TENX, USB_DEVICE_ID_TENX_IBUDDY1) }, + { HID_USB_DEVICE(USB_VENDOR_ID_TENX, USB_DEVICE_ID_TENX_IBUDDY2) }, + { HID_USB_DEVICE(USB_VENDOR_ID_THRUSTMASTER, 0xb300) }, + { HID_USB_DEVICE(USB_VENDOR_ID_THRUSTMASTER, 0xb304) }, + { HID_USB_DEVICE(USB_VENDOR_ID_THRUSTMASTER, 0xb651) }, + { HID_USB_DEVICE(USB_VENDOR_ID_THRUSTMASTER, 0xb654) }, + { HID_USB_DEVICE(USB_VENDOR_ID_VERNIER, USB_DEVICE_ID_VERNIER_LABPRO) }, + { HID_USB_DEVICE(USB_VENDOR_ID_VERNIER, USB_DEVICE_ID_VERNIER_GOTEMP) }, + { HID_USB_DEVICE(USB_VENDOR_ID_VERNIER, USB_DEVICE_ID_VERNIER_SKIP) }, + { HID_USB_DEVICE(USB_VENDOR_ID_VERNIER, USB_DEVICE_ID_VERNIER_CYCLOPS) }, + { HID_USB_DEVICE(USB_VENDOR_ID_VERNIER, USB_DEVICE_ID_VERNIER_LCSPEC) }, + { HID_USB_DEVICE(USB_VENDOR_ID_WACOM, HID_ANY_ID) }, + { HID_USB_DEVICE(USB_VENDOR_ID_WISEGROUP, USB_DEVICE_ID_4_PHIDGETSERVO_20) }, + { HID_USB_DEVICE(USB_VENDOR_ID_WISEGROUP, USB_DEVICE_ID_1_PHIDGETSERVO_20) }, + { HID_USB_DEVICE(USB_VENDOR_ID_WISEGROUP, USB_DEVICE_ID_8_8_4_IF_KIT) }, + { HID_USB_DEVICE(USB_VENDOR_ID_YEALINK, USB_DEVICE_ID_YEALINK_P1K_P4K_B2K) }, + { HID_USB_DEVICE(USB_VENDOR_ID_ZEROPLUS, 0x0005) }, + { HID_USB_DEVICE(USB_VENDOR_ID_ZEROPLUS, 0x0030) }, + { } +}; + +static bool hid_ignore(struct hid_device *hdev) +{ + switch (hdev->vendor) { + case USB_VENDOR_ID_CODEMERCS: + /* ignore all Code Mercenaries IOWarrior devices */ + if (hdev->product >= USB_DEVICE_ID_CODEMERCS_IOW_FIRST && + hdev->product <= USB_DEVICE_ID_CODEMERCS_IOW_LAST) + return true; + break; + case USB_VENDOR_ID_LOGITECH: + if (hdev->product >= USB_DEVICE_ID_LOGITECH_HARMONY_FIRST && + hdev->product <= USB_DEVICE_ID_LOGITECH_HARMONY_LAST) + return true; + break; + } + + return !!hid_match_id(hdev, hid_ignore_list); +} + +int hid_add_device(struct hid_device *hdev) +{ + static atomic_t id = ATOMIC_INIT(0); + int ret; + + if (WARN_ON(hdev->status & HID_STAT_ADDED)) + return -EBUSY; + + /* we need to kill them here, otherwise they will stay allocated to + * wait for coming driver */ + if (hid_ignore(hdev)) + return -ENODEV; + + /* XXX hack, any other cleaner solution < 20 bus_id bytes? */ + sprintf(hdev->dev.bus_id, "%04X:%04X:%04X.%04X", hdev->bus, + hdev->vendor, hdev->product, atomic_inc_return(&id)); + + ret = device_add(&hdev->dev); + if (!ret) + hdev->status |= HID_STAT_ADDED; + + return ret; +} +EXPORT_SYMBOL_GPL(hid_add_device); + +/** + * hid_allocate_device - allocate new hid device descriptor + * + * Allocate and initialize hid device, so that hid_destroy_device might be + * used to free it. + * + * New hid_device pointer is returned on success, otherwise ERR_PTR encoded + * error value. + */ +struct hid_device *hid_allocate_device(void) +{ + struct hid_device *hdev; + unsigned int i; + int ret = -ENOMEM; + + hdev = kzalloc(sizeof(*hdev), GFP_KERNEL); + if (hdev == NULL) + return ERR_PTR(ret); + + device_initialize(&hdev->dev); + hdev->dev.release = hid_device_release; + hdev->dev.bus = &hid_bus_type; + + hdev->collection = kcalloc(HID_DEFAULT_NUM_COLLECTIONS, + sizeof(struct hid_collection), GFP_KERNEL); + if (hdev->collection == NULL) + goto err; + hdev->collection_size = HID_DEFAULT_NUM_COLLECTIONS; + + for (i = 0; i < HID_REPORT_TYPES; i++) + INIT_LIST_HEAD(&hdev->report_enum[i].report_list); + + return hdev; +err: + put_device(&hdev->dev); + return ERR_PTR(ret); +} +EXPORT_SYMBOL_GPL(hid_allocate_device); + +static void hid_remove_device(struct hid_device *hdev) +{ + if (hdev->status & HID_STAT_ADDED) { + device_del(&hdev->dev); + hdev->status &= ~HID_STAT_ADDED; + } +} + +/** + * hid_destroy_device - free previously allocated device + * + * @hdev: hid device + * + * If you allocate hid_device through hid_allocate_device, you should ever + * free by this function. + */ +void hid_destroy_device(struct hid_device *hdev) +{ + hid_remove_device(hdev); + put_device(&hdev->dev); +} +EXPORT_SYMBOL_GPL(hid_destroy_device); + +int __hid_register_driver(struct hid_driver *hdrv, struct module *owner, + const char *mod_name) +{ + hdrv->driver.name = hdrv->name; + hdrv->driver.bus = &hid_bus_type; + hdrv->driver.owner = owner; + hdrv->driver.mod_name = mod_name; + + return driver_register(&hdrv->driver); +} +EXPORT_SYMBOL_GPL(__hid_register_driver); + +void hid_unregister_driver(struct hid_driver *hdrv) +{ + driver_unregister(&hdrv->driver); +} +EXPORT_SYMBOL_GPL(hid_unregister_driver); + +#ifdef CONFIG_HID_COMPAT +static void hid_compat_load(struct work_struct *ws) +{ + request_module("hid-dummy"); +} +static DECLARE_WORK(hid_compat_work, hid_compat_load); +static struct workqueue_struct *hid_compat_wq; +#endif + static int __init hid_init(void) { - return hidraw_init(); + int ret; + + ret = bus_register(&hid_bus_type); + if (ret) { + printk(KERN_ERR "HID: can't register hid bus\n"); + goto err; + } + + ret = hidraw_init(); + if (ret) + goto err_bus; + +#ifdef CONFIG_HID_COMPAT + hid_compat_wq = create_workqueue("hid_compat"); + if (!hid_compat_wq) { + hidraw_exit(); + goto err; + } + queue_work(hid_compat_wq, &hid_compat_work); +#endif + + return 0; +err_bus: + bus_unregister(&hid_bus_type); +err: + return ret; } static void __exit hid_exit(void) { +#ifdef CONFIG_HID_COMPAT + destroy_workqueue(hid_compat_wq); +#endif hidraw_exit(); + bus_unregister(&hid_bus_type); } module_init(hid_init); diff --git a/drivers/hid/hid-cypress.c b/drivers/hid/hid-cypress.c new file mode 100644 index 000000000000..5d69d27b935d --- /dev/null +++ b/drivers/hid/hid-cypress.c @@ -0,0 +1,158 @@ +/* + * HID driver for some cypress "special" devices + * + * 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) 2006-2007 Jiri Kosina + * Copyright (c) 2007 Paul Walmsley + * Copyright (c) 2008 Jiri Slaby + */ + +/* + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the Free + * Software Foundation; either version 2 of the License, or (at your option) + * any later version. + */ + +#include <linux/device.h> +#include <linux/hid.h> +#include <linux/input.h> +#include <linux/module.h> + +#include "hid-ids.h" + +#define CP_RDESC_SWAPPED_MIN_MAX 0x01 +#define CP_2WHEEL_MOUSE_HACK 0x02 +#define CP_2WHEEL_MOUSE_HACK_ON 0x04 + +/* + * Some USB barcode readers from cypress have usage min and usage max in + * the wrong order + */ +static void cp_report_fixup(struct hid_device *hdev, __u8 *rdesc, + unsigned int rsize) +{ + unsigned long quirks = (unsigned long)hid_get_drvdata(hdev); + unsigned int i; + + if (!(quirks & CP_RDESC_SWAPPED_MIN_MAX)) + return; + + for (i = 0; i < rsize - 4; i++) + if (rdesc[i] == 0x29 && rdesc[i + 2] == 0x19) { + __u8 tmp; + + rdesc[i] = 0x19; + rdesc[i + 2] = 0x29; + tmp = rdesc[i + 3]; + rdesc[i + 3] = rdesc[i + 1]; + rdesc[i + 1] = tmp; + } +} + +static int cp_input_mapped(struct hid_device *hdev, struct hid_input *hi, + struct hid_field *field, struct hid_usage *usage, + unsigned long **bit, int *max) +{ + unsigned long quirks = (unsigned long)hid_get_drvdata(hdev); + + if (!(quirks & CP_2WHEEL_MOUSE_HACK)) + return 0; + + if (usage->type == EV_REL && usage->code == REL_WHEEL) + set_bit(REL_HWHEEL, *bit); + if (usage->hid == 0x00090005) + return -1; + + return 0; +} + +static int cp_event(struct hid_device *hdev, struct hid_field *field, + struct hid_usage *usage, __s32 value) +{ + unsigned long quirks = (unsigned long)hid_get_drvdata(hdev); + + if (!(hdev->claimed & HID_CLAIMED_INPUT) || !field->hidinput || + !usage->type || !(quirks & CP_2WHEEL_MOUSE_HACK)) + return 0; + + if (usage->hid == 0x00090005) { + if (value) + quirks |= CP_2WHEEL_MOUSE_HACK_ON; + else + quirks &= ~CP_2WHEEL_MOUSE_HACK_ON; + hid_set_drvdata(hdev, (void *)quirks); + return 1; + } + + if (usage->code == REL_WHEEL && (quirks & CP_2WHEEL_MOUSE_HACK_ON)) { + struct input_dev *input = field->hidinput->input; + + input_event(input, usage->type, REL_HWHEEL, value); + return 1; + } + + return 0; +} + +static int cp_probe(struct hid_device *hdev, const struct hid_device_id *id) +{ + unsigned long quirks = id->driver_data; + int ret; + + hid_set_drvdata(hdev, (void *)quirks); + + ret = hid_parse(hdev); + if (ret) { + dev_err(&hdev->dev, "parse failed\n"); + goto err_free; + } + + ret = hid_hw_start(hdev, HID_CONNECT_DEFAULT); + if (ret) { + dev_err(&hdev->dev, "hw start failed\n"); + goto err_free; + } + + return 0; +err_free: + return ret; +} + +static const struct hid_device_id cp_devices[] = { + { HID_USB_DEVICE(USB_VENDOR_ID_CYPRESS, USB_DEVICE_ID_CYPRESS_BARCODE_1), + .driver_data = CP_RDESC_SWAPPED_MIN_MAX }, + { HID_USB_DEVICE(USB_VENDOR_ID_CYPRESS, USB_DEVICE_ID_CYPRESS_BARCODE_2), + .driver_data = CP_RDESC_SWAPPED_MIN_MAX }, + { HID_USB_DEVICE(USB_VENDOR_ID_CYPRESS, USB_DEVICE_ID_CYPRESS_MOUSE), + .driver_data = CP_2WHEEL_MOUSE_HACK }, + { } +}; +MODULE_DEVICE_TABLE(hid, cp_devices); + +static struct hid_driver cp_driver = { + .name = "cypress", + .id_table = cp_devices, + .report_fixup = cp_report_fixup, + .input_mapped = cp_input_mapped, + .event = cp_event, + .probe = cp_probe, +}; + +static int cp_init(void) +{ + return hid_register_driver(&cp_driver); +} + +static void cp_exit(void) +{ + hid_unregister_driver(&cp_driver); +} + +module_init(cp_init); +module_exit(cp_exit); +MODULE_LICENSE("GPL"); + +HID_COMPAT_LOAD_DRIVER(cypress); diff --git a/drivers/hid/hid-dell.c b/drivers/hid/hid-dell.c new file mode 100644 index 000000000000..1a0d0dfc62fc --- /dev/null +++ b/drivers/hid/hid-dell.c @@ -0,0 +1,75 @@ +/* + * HID driver for some dell "special" devices + * + * 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) 2006-2007 Jiri Kosina + * Copyright (c) 2007 Paul Walmsley + * Copyright (c) 2008 Jiri Slaby + */ + +/* + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the Free + * Software Foundation; either version 2 of the License, or (at your option) + * any later version. + */ + +#include <linux/device.h> +#include <linux/hid.h> +#include <linux/module.h> + +#include "hid-ids.h" + +static int dell_probe(struct hid_device *hdev, const struct hid_device_id *id) +{ + int ret; + + ret = hid_parse(hdev); + if (ret) { + dev_err(&hdev->dev, "parse failed\n"); + goto err_free; + } + + ret = hid_hw_start(hdev, HID_CONNECT_DEFAULT); + if (ret) { + dev_err(&hdev->dev, "hw start failed\n"); + goto err_free; + } + + usbhid_set_leds(hdev); + + return 0; +err_free: + return ret; +} + +static const struct hid_device_id dell_devices[] = { + { HID_USB_DEVICE(USB_VENDOR_ID_DELL, USB_DEVICE_ID_DELL_W7658) }, + { HID_USB_DEVICE(USB_VENDOR_ID_DELL, USB_DEVICE_ID_DELL_SK8115) }, + { } +}; +MODULE_DEVICE_TABLE(hid, dell_devices); + +static struct hid_driver dell_driver = { + .name = "dell", + .id_table = dell_devices, + .probe = dell_probe, +}; + +static int dell_init(void) +{ + return hid_register_driver(&dell_driver); +} + +static void dell_exit(void) +{ + hid_unregister_driver(&dell_driver); +} + +module_init(dell_init); +module_exit(dell_exit); +MODULE_LICENSE("GPL"); + +HID_COMPAT_LOAD_DRIVER(dell); diff --git a/drivers/hid/hid-dummy.c b/drivers/hid/hid-dummy.c new file mode 100644 index 000000000000..e148f86fb58e --- /dev/null +++ b/drivers/hid/hid-dummy.c @@ -0,0 +1,72 @@ +#include <linux/autoconf.h> +#include <linux/module.h> +#include <linux/hid.h> + +static int __init hid_dummy_init(void) +{ +#ifdef CONFIG_HID_A4TECH_MODULE + HID_COMPAT_CALL_DRIVER(a4tech); +#endif +#ifdef CONFIG_HID_APPLE_MODULE + HID_COMPAT_CALL_DRIVER(apple); +#endif +#ifdef CONFIG_HID_BELKIN_MODULE + HID_COMPAT_CALL_DRIVER(belkin); +#endif +#ifdef CONFIG_HID_BRIGHT_MODULE + HID_COMPAT_CALL_DRIVER(bright); +#endif +#ifdef CONFIG_HID_CHERRY_MODULE + HID_COMPAT_CALL_DRIVER(cherry); +#endif +#ifdef CONFIG_HID_CHICONY_MODULE + HID_COMPAT_CALL_DRIVER(chicony); +#endif +#ifdef CONFIG_HID_CYPRESS_MODULE + HID_COMPAT_CALL_DRIVER(cypress); +#endif +#ifdef CONFIG_HID_DELL_MODULE + HID_COMPAT_CALL_DRIVER(dell); +#endif +#ifdef CONFIG_HID_EZKEY_MODULE + HID_COMPAT_CALL_DRIVER(ezkey); +#endif +#ifdef CONFIG_HID_GYRATION_MODULE + HID_COMPAT_CALL_DRIVER(gyration); +#endif +#ifdef CONFIG_HID_LOGITECH_MODULE + HID_COMPAT_CALL_DRIVER(logitech); +#endif +#ifdef CONFIG_HID_MICROSOFT_MODULE + HID_COMPAT_CALL_DRIVER(microsoft); +#endif +#ifdef CONFIG_HID_MONTEREY_MODULE + HID_COMPAT_CALL_DRIVER(monterey); +#endif +#ifdef CONFIG_HID_PANTHERLORD_MODULE + HID_COMPAT_CALL_DRIVER(pantherlord); +#endif +#ifdef CONFIG_HID_PETALYNX_MODULE + HID_COMPAT_CALL_DRIVER(petalynx); +#endif +#ifdef CONFIG_HID_SAMSUNG_MODULE + HID_COMPAT_CALL_DRIVER(samsung); +#endif +#ifdef CONFIG_HID_SONY_MODULE + HID_COMPAT_CALL_DRIVER(sony); +#endif +#ifdef CONFIG_HID_SUNPLUS_MODULE + HID_COMPAT_CALL_DRIVER(sunplus); +#endif +#ifdef CONFIG_THRUSTMASTER_FF_MODULE + HID_COMPAT_CALL_DRIVER(thrustmaster); +#endif +#ifdef CONFIG_ZEROPLUS_FF_MODULE + HID_COMPAT_CALL_DRIVER(zeroplus); +#endif + + return -EIO; +} +module_init(hid_dummy_init); + +MODULE_LICENSE("GPL"); diff --git a/drivers/hid/hid-ezkey.c b/drivers/hid/hid-ezkey.c new file mode 100644 index 000000000000..deb42f931b7e --- /dev/null +++ b/drivers/hid/hid-ezkey.c @@ -0,0 +1,95 @@ +/* + * HID driver for some ezkey "special" devices + * + * 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) 2006-2007 Jiri Kosina + * Copyright (c) 2007 Paul Walmsley + * Copyright (c) 2008 Jiri Slaby + */ + +/* + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the Free + * Software Foundation; either version 2 of the License, or (at your option) + * any later version. + */ + +#include <linux/device.h> +#include <linux/input.h> +#include <linux/hid.h> +#include <linux/module.h> + +#include "hid-ids.h" + +#define ez_map_rel(c) hid_map_usage(hi, usage, bit, max, EV_REL, (c)) +#define ez_map_key(c) hid_map_usage(hi, usage, bit, max, EV_KEY, (c)) + +static int ez_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) != HID_UP_CONSUMER) + return 0; + + switch (usage->hid & HID_USAGE) { + case 0x230: ez_map_key(BTN_MOUSE); break; + case 0x231: ez_map_rel(REL_WHEEL); break; + /* + * this keyboard has a scrollwheel implemented in + * totally broken way. We map this usage temporarily + * to HWHEEL and handle it in the event quirk handler + */ + case 0x232: ez_map_rel(REL_HWHEEL); break; + default: + return 0; + } + return 1; +} + +static int ez_event(struct hid_device *hdev, struct hid_field *field, + struct hid_usage *usage, __s32 value) +{ + if (!(hdev->claimed & HID_CLAIMED_INPUT) || !field->hidinput || + !usage->type) + return 0; + + /* handle the temporary quirky mapping to HWHEEL */ + if (usage->type == EV_REL && usage->code == REL_HWHEEL) { + struct input_dev *input = field->hidinput->input; + input_event(input, usage->type, REL_WHEEL, -value); + return 1; + } + + return 0; +} + +static const struct hid_device_id ez_devices[] = { + { HID_USB_DEVICE(USB_VENDOR_ID_EZKEY, USB_DEVICE_ID_BTC_8193) }, + { } +}; +MODULE_DEVICE_TABLE(hid, ez_devices); + +static struct hid_driver ez_driver = { + .name = "ezkey", + .id_table = ez_devices, + .input_mapping = ez_input_mapping, + .event = ez_event, +}; + +static int ez_init(void) +{ + return hid_register_driver(&ez_driver); +} + +static void ez_exit(void) +{ + hid_unregister_driver(&ez_driver); +} + +module_init(ez_init); +module_exit(ez_exit); +MODULE_LICENSE("GPL"); + +HID_COMPAT_LOAD_DRIVER(ezkey); diff --git a/drivers/hid/hid-gyration.c b/drivers/hid/hid-gyration.c new file mode 100644 index 000000000000..ac5120f542cc --- /dev/null +++ b/drivers/hid/hid-gyration.c @@ -0,0 +1,96 @@ +/* + * HID driver for some gyration "special" devices + * + * 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) 2006-2007 Jiri Kosina + * Copyright (c) 2007 Paul Walmsley + * Copyright (c) 2008 Jiri Slaby + */ + +/* + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the Free + * Software Foundation; either version 2 of the License, or (at your option) + * any later version. + */ + +#include <linux/device.h> +#include <linux/input.h> +#include <linux/hid.h> +#include <linux/module.h> + +#include "hid-ids.h" + +#define gy_map_key_clear(c) hid_map_usage_clear(hi, usage, bit, max, \ + EV_KEY, (c)) +static int gyration_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) != HID_UP_LOGIVENDOR) + return 0; + + set_bit(EV_REP, hi->input->evbit); + switch (usage->hid & HID_USAGE) { + /* Reported on Gyration MCE Remote */ + case 0x00d: gy_map_key_clear(KEY_HOME); break; + case 0x024: gy_map_key_clear(KEY_DVD); break; + case 0x025: gy_map_key_clear(KEY_PVR); break; + case 0x046: gy_map_key_clear(KEY_MEDIA); break; + case 0x047: gy_map_key_clear(KEY_MP3); break; + case 0x049: gy_map_key_clear(KEY_CAMERA); break; + case 0x04a: gy_map_key_clear(KEY_VIDEO); break; + + default: + return 0; + } + return 1; +} + +static int gyration_event(struct hid_device *hdev, struct hid_field *field, + struct hid_usage *usage, __s32 value) +{ + struct input_dev *input = field->hidinput->input; + + if ((usage->hid & HID_USAGE_PAGE) == HID_UP_GENDESK && + (usage->hid & 0xff) == 0x82) { + input_event(input, usage->type, usage->code, 1); + input_sync(input); + input_event(input, usage->type, usage->code, 0); + input_sync(input); + return 1; + } + + return 0; +} + +static const struct hid_device_id gyration_devices[] = { + { HID_USB_DEVICE(USB_VENDOR_ID_GYRATION, USB_DEVICE_ID_GYRATION_REMOTE) }, + { } +}; +MODULE_DEVICE_TABLE(hid, gyration_devices); + +static struct hid_driver gyration_driver = { + .name = "gyration", + .id_table = gyration_devices, + .input_mapping = gyration_input_mapping, + .event = gyration_event, +}; + +static int gyration_init(void) +{ + return hid_register_driver(&gyration_driver); +} + +static void gyration_exit(void) +{ + hid_unregister_driver(&gyration_driver); +} + +module_init(gyration_init); +module_exit(gyration_exit); +MODULE_LICENSE("GPL"); + +HID_COMPAT_LOAD_DRIVER(gyration); diff --git a/drivers/hid/hid-ids.h b/drivers/hid/hid-ids.h new file mode 100644 index 000000000000..aad9ed1b406e --- /dev/null +++ b/drivers/hid/hid-ids.h @@ -0,0 +1,404 @@ +/* + * USB HID quirks support for Linux + * + * 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) 2006-2007 Jiri Kosina + * Copyright (c) 2007 Paul Walmsley + */ + +/* + * 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 HID_IDS_H_FILE +#define HID_IDS_H_FILE + +#define USB_VENDOR_ID_A4TECH 0x09da +#define USB_DEVICE_ID_A4TECH_WCP32PU 0x0006 +#define USB_DEVICE_ID_A4TECH_X5_005D 0x000a + +#define USB_VENDOR_ID_AASHIMA 0x06d6 +#define USB_DEVICE_ID_AASHIMA_GAMEPAD 0x0025 +#define USB_DEVICE_ID_AASHIMA_PREDATOR 0x0026 + +#define USB_VENDOR_ID_ACECAD 0x0460 +#define USB_DEVICE_ID_ACECAD_FLAIR 0x0004 +#define USB_DEVICE_ID_ACECAD_302 0x0008 + +#define USB_VENDOR_ID_ADS_TECH 0x06e1 +#define USB_DEVICE_ID_ADS_TECH_RADIO_SI470X 0xa155 + +#define USB_VENDOR_ID_AFATECH 0x15a4 +#define USB_DEVICE_ID_AFATECH_AF9016 0x9016 + +#define USB_VENDOR_ID_AIPTEK 0x08ca +#define USB_DEVICE_ID_AIPTEK_01 0x0001 +#define USB_DEVICE_ID_AIPTEK_10 0x0010 +#define USB_DEVICE_ID_AIPTEK_20 0x0020 +#define USB_DEVICE_ID_AIPTEK_21 0x0021 +#define USB_DEVICE_ID_AIPTEK_22 0x0022 +#define USB_DEVICE_ID_AIPTEK_23 0x0023 +#define USB_DEVICE_ID_AIPTEK_24 0x0024 + +#define USB_VENDOR_ID_AIRCABLE 0x16CA +#define USB_DEVICE_ID_AIRCABLE1 0x1502 + +#define USB_VENDOR_ID_ALCOR 0x058f +#define USB_DEVICE_ID_ALCOR_USBRS232 0x9720 + +#define USB_VENDOR_ID_ALPS 0x0433 +#define USB_DEVICE_ID_IBM_GAMEPAD 0x1101 + +#define USB_VENDOR_ID_APPLE 0x05ac +#define USB_DEVICE_ID_APPLE_MIGHTYMOUSE 0x0304 +#define USB_DEVICE_ID_APPLE_FOUNTAIN_ANSI 0x020e +#define USB_DEVICE_ID_APPLE_FOUNTAIN_ISO 0x020f +#define USB_DEVICE_ID_APPLE_GEYSER_ANSI 0x0214 +#define USB_DEVICE_ID_APPLE_GEYSER_ISO 0x0215 +#define USB_DEVICE_ID_APPLE_GEYSER_JIS 0x0216 +#define USB_DEVICE_ID_APPLE_GEYSER3_ANSI 0x0217 +#define USB_DEVICE_ID_APPLE_GEYSER3_ISO 0x0218 +#define USB_DEVICE_ID_APPLE_GEYSER3_JIS 0x0219 +#define USB_DEVICE_ID_APPLE_GEYSER4_ANSI 0x021a +#define USB_DEVICE_ID_APPLE_GEYSER4_ISO 0x021b +#define USB_DEVICE_ID_APPLE_GEYSER4_JIS 0x021c +#define USB_DEVICE_ID_APPLE_ALU_ANSI 0x0220 +#define USB_DEVICE_ID_APPLE_ALU_ISO 0x0221 +#define USB_DEVICE_ID_APPLE_ALU_JIS 0x0222 +#define USB_DEVICE_ID_APPLE_WELLSPRING_ANSI 0x0223 +#define USB_DEVICE_ID_APPLE_WELLSPRING_ISO 0x0224 +#define USB_DEVICE_ID_APPLE_WELLSPRING_JIS 0x0225 +#define USB_DEVICE_ID_APPLE_GEYSER4_HF_ANSI 0x0229 +#define USB_DEVICE_ID_APPLE_GEYSER4_HF_ISO 0x022a +#define USB_DEVICE_ID_APPLE_GEYSER4_HF_JIS 0x022b +#define USB_DEVICE_ID_APPLE_ALU_WIRELESS_ANSI 0x022c +#define USB_DEVICE_ID_APPLE_ALU_WIRELESS_ISO 0x022d +#define USB_DEVICE_ID_APPLE_ALU_WIRELESS_JIS 0x022e +#define USB_DEVICE_ID_APPLE_WELLSPRING2_ANSI 0x0230 +#define USB_DEVICE_ID_APPLE_WELLSPRING2_ISO 0x0231 +#define USB_DEVICE_ID_APPLE_WELLSPRING2_JIS 0x0232 +#define USB_DEVICE_ID_APPLE_FOUNTAIN_TP_ONLY 0x030a +#define USB_DEVICE_ID_APPLE_GEYSER1_TP_ONLY 0x030b +#define USB_DEVICE_ID_APPLE_ATV_IRCONTROL 0x8241 +#define USB_DEVICE_ID_APPLE_IRCONTROL4 0x8242 + +#define USB_VENDOR_ID_ASUS 0x0b05 +#define USB_DEVICE_ID_ASUS_LCM 0x1726 + +#define USB_VENDOR_ID_ATEN 0x0557 +#define USB_DEVICE_ID_ATEN_UC100KM 0x2004 +#define USB_DEVICE_ID_ATEN_CS124U 0x2202 +#define USB_DEVICE_ID_ATEN_2PORTKVM 0x2204 +#define USB_DEVICE_ID_ATEN_4PORTKVM 0x2205 +#define USB_DEVICE_ID_ATEN_4PORTKVMC 0x2208 + +#define USB_VENDOR_ID_AVERMEDIA 0x07ca +#define USB_DEVICE_ID_AVER_FM_MR800 0xb800 + +#define USB_VENDOR_ID_BELKIN 0x050d +#define USB_DEVICE_ID_FLIP_KVM 0x3201 + +#define USB_VENDOR_ID_BRIGHT 0x1241 +#define USB_DEVICE_ID_BRIGHT_ABNT2 0x1503 + +#define USB_VENDOR_ID_BERKSHIRE 0x0c98 +#define USB_DEVICE_ID_BERKSHIRE_PCWD 0x1140 + +#define USB_VENDOR_ID_CHERRY 0x046a +#define USB_DEVICE_ID_CHERRY_CYMOTION 0x0023 + +#define USB_VENDOR_ID_CHIC 0x05fe +#define USB_DEVICE_ID_CHIC_GAMEPAD 0x0014 + +#define USB_VENDOR_ID_CHICONY 0x04f2 +#define USB_DEVICE_ID_CHICONY_TACTICAL_PAD 0x0418 + +#define USB_VENDOR_ID_CIDC 0x1677 + +#define USB_VENDOR_ID_CMEDIA 0x0d8c +#define USB_DEVICE_ID_CM109 0x000e + +#define USB_VENDOR_ID_CODEMERCS 0x07c0 +#define USB_DEVICE_ID_CODEMERCS_IOW_FIRST 0x1500 +#define USB_DEVICE_ID_CODEMERCS_IOW_LAST 0x15ff + +#define USB_VENDOR_ID_CYGNAL 0x10c4 +#define USB_DEVICE_ID_CYGNAL_RADIO_SI470X 0x818a + +#define USB_VENDOR_ID_CYPRESS 0x04b4 +#define USB_DEVICE_ID_CYPRESS_MOUSE 0x0001 +#define USB_DEVICE_ID_CYPRESS_HIDCOM 0x5500 +#define USB_DEVICE_ID_CYPRESS_ULTRAMOUSE 0x7417 +#define USB_DEVICE_ID_CYPRESS_BARCODE_1 0xde61 +#define USB_DEVICE_ID_CYPRESS_BARCODE_2 0xde64 + +#define USB_VENDOR_ID_DELL 0x413c +#define USB_DEVICE_ID_DELL_W7658 0x2005 +#define USB_DEVICE_ID_DELL_SK8115 0x2105 + +#define USB_VENDOR_ID_DELORME 0x1163 +#define USB_DEVICE_ID_DELORME_EARTHMATE 0x0100 +#define USB_DEVICE_ID_DELORME_EM_LT20 0x0200 + +#define USB_VENDOR_ID_DMI 0x0c0b +#define USB_DEVICE_ID_DMI_ENC 0x5fab + +#define USB_VENDOR_ID_ELO 0x04E7 +#define USB_DEVICE_ID_ELO_TS2700 0x0020 + +#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_GAMERON 0x0810 +#define USB_DEVICE_ID_GAMERON_DUAL_PSX_ADAPTOR 0x0001 + +#define USB_VENDOR_ID_GENERAL_TOUCH 0x0dfc + +#define USB_VENDOR_ID_GLAB 0x06c2 +#define USB_DEVICE_ID_4_PHIDGETSERVO_30 0x0038 +#define USB_DEVICE_ID_1_PHIDGETSERVO_30 0x0039 +#define USB_DEVICE_ID_0_0_4_IF_KIT 0x0040 +#define USB_DEVICE_ID_0_16_16_IF_KIT 0x0044 +#define USB_DEVICE_ID_8_8_8_IF_KIT 0x0045 +#define USB_DEVICE_ID_0_8_7_IF_KIT 0x0051 +#define USB_DEVICE_ID_0_8_8_IF_KIT 0x0053 +#define USB_DEVICE_ID_PHIDGET_MOTORCONTROL 0x0058 + +#define USB_VENDOR_ID_GOTOP 0x08f2 +#define USB_DEVICE_ID_SUPER_Q2 0x007f +#define USB_DEVICE_ID_GOGOPEN 0x00ce +#define USB_DEVICE_ID_PENPOWER 0x00f4 + +#define USB_VENDOR_ID_GREENASIA 0x0e8f + +#define USB_VENDOR_ID_GRETAGMACBETH 0x0971 +#define USB_DEVICE_ID_GRETAGMACBETH_HUEY 0x2005 + +#define USB_VENDOR_ID_GRIFFIN 0x077d +#define USB_DEVICE_ID_POWERMATE 0x0410 +#define USB_DEVICE_ID_SOUNDKNOB 0x04AA + +#define USB_VENDOR_ID_GTCO 0x078c +#define USB_DEVICE_ID_GTCO_90 0x0090 +#define USB_DEVICE_ID_GTCO_100 0x0100 +#define USB_DEVICE_ID_GTCO_101 0x0101 +#define USB_DEVICE_ID_GTCO_103 0x0103 +#define USB_DEVICE_ID_GTCO_104 0x0104 +#define USB_DEVICE_ID_GTCO_105 0x0105 +#define USB_DEVICE_ID_GTCO_106 0x0106 +#define USB_DEVICE_ID_GTCO_107 0x0107 +#define USB_DEVICE_ID_GTCO_108 0x0108 +#define USB_DEVICE_ID_GTCO_200 0x0200 +#define USB_DEVICE_ID_GTCO_201 0x0201 +#define USB_DEVICE_ID_GTCO_202 0x0202 +#define USB_DEVICE_ID_GTCO_203 0x0203 +#define USB_DEVICE_ID_GTCO_204 0x0204 +#define USB_DEVICE_ID_GTCO_205 0x0205 +#define USB_DEVICE_ID_GTCO_206 0x0206 +#define USB_DEVICE_ID_GTCO_207 0x0207 +#define USB_DEVICE_ID_GTCO_300 0x0300 +#define USB_DEVICE_ID_GTCO_301 0x0301 +#define USB_DEVICE_ID_GTCO_302 0x0302 +#define USB_DEVICE_ID_GTCO_303 0x0303 +#define USB_DEVICE_ID_GTCO_304 0x0304 +#define USB_DEVICE_ID_GTCO_305 0x0305 +#define USB_DEVICE_ID_GTCO_306 0x0306 +#define USB_DEVICE_ID_GTCO_307 0x0307 +#define USB_DEVICE_ID_GTCO_308 0x0308 +#define USB_DEVICE_ID_GTCO_309 0x0309 +#define USB_DEVICE_ID_GTCO_400 0x0400 +#define USB_DEVICE_ID_GTCO_401 0x0401 +#define USB_DEVICE_ID_GTCO_402 0x0402 +#define USB_DEVICE_ID_GTCO_403 0x0403 +#define USB_DEVICE_ID_GTCO_404 0x0404 +#define USB_DEVICE_ID_GTCO_405 0x0405 +#define USB_DEVICE_ID_GTCO_500 0x0500 +#define USB_DEVICE_ID_GTCO_501 0x0501 +#define USB_DEVICE_ID_GTCO_502 0x0502 +#define USB_DEVICE_ID_GTCO_503 0x0503 +#define USB_DEVICE_ID_GTCO_504 0x0504 +#define USB_DEVICE_ID_GTCO_1000 0x1000 +#define USB_DEVICE_ID_GTCO_1001 0x1001 +#define USB_DEVICE_ID_GTCO_1002 0x1002 +#define USB_DEVICE_ID_GTCO_1003 0x1003 +#define USB_DEVICE_ID_GTCO_1004 0x1004 +#define USB_DEVICE_ID_GTCO_1005 0x1005 +#define USB_DEVICE_ID_GTCO_1006 0x1006 +#define USB_DEVICE_ID_GTCO_1007 0x1007 + +#define USB_VENDOR_ID_GYRATION 0x0c16 +#define USB_DEVICE_ID_GYRATION_REMOTE 0x0002 + +#define USB_VENDOR_ID_HAPP 0x078b +#define USB_DEVICE_ID_UGCI_DRIVING 0x0010 +#define USB_DEVICE_ID_UGCI_FLYING 0x0020 +#define USB_DEVICE_ID_UGCI_FIGHTING 0x0030 + +#define USB_VENDOR_ID_IMATION 0x0718 +#define USB_DEVICE_ID_DISC_STAKKA 0xd000 + +#define USB_VENDOR_ID_KBGEAR 0x084e +#define USB_DEVICE_ID_KBGEAR_JAMSTUDIO 0x1001 + +#define USB_VENDOR_ID_LABTEC 0x1020 +#define USB_DEVICE_ID_LABTEC_WIRELESS_KEYBOARD 0x0006 + +#define USB_VENDOR_ID_LD 0x0f11 +#define USB_DEVICE_ID_LD_CASSY 0x1000 +#define USB_DEVICE_ID_LD_POCKETCASSY 0x1010 +#define USB_DEVICE_ID_LD_MOBILECASSY 0x1020 +#define USB_DEVICE_ID_LD_JWM 0x1080 +#define USB_DEVICE_ID_LD_DMMP 0x1081 +#define USB_DEVICE_ID_LD_UMIP 0x1090 +#define USB_DEVICE_ID_LD_XRAY1 0x1100 +#define USB_DEVICE_ID_LD_XRAY2 0x1101 +#define USB_DEVICE_ID_LD_VIDEOCOM 0x1200 +#define USB_DEVICE_ID_LD_COM3LAB 0x2000 +#define USB_DEVICE_ID_LD_TELEPORT 0x2010 +#define USB_DEVICE_ID_LD_NETWORKANALYSER 0x2020 +#define USB_DEVICE_ID_LD_POWERCONTROL 0x2030 +#define USB_DEVICE_ID_LD_MACHINETEST 0x2040 + +#define USB_VENDOR_ID_LOGITECH 0x046d +#define USB_DEVICE_ID_LOGITECH_LX3 0xc044 +#define USB_DEVICE_ID_LOGITECH_V150 0xc047 +#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_RUMBLEPAD 0xc211 +#define USB_DEVICE_ID_LOGITECH_EXTREME_3D 0xc215 +#define USB_DEVICE_ID_LOGITECH_RUMBLEPAD2 0xc218 +#define USB_DEVICE_ID_LOGITECH_RUMBLEPAD2_2 0xc219 +#define USB_DEVICE_ID_LOGITECH_WINGMAN_F3D 0xc283 +#define USB_DEVICE_ID_LOGITECH_FORCE3D_PRO 0xc286 +#define USB_DEVICE_ID_LOGITECH_WHEEL 0xc294 +#define USB_DEVICE_ID_LOGITECH_MOMO_WHEEL 0xc295 +#define USB_DEVICE_ID_LOGITECH_ELITE_KBD 0xc30a +#define USB_DEVICE_ID_LOGITECH_KBD 0xc311 +#define USB_DEVICE_ID_S510_RECEIVER 0xc50c +#define USB_DEVICE_ID_S510_RECEIVER_2 0xc517 +#define USB_DEVICE_ID_LOGITECH_CORDLESS_DESKTOP_LX500 0xc512 +#define USB_DEVICE_ID_MX3000_RECEIVER 0xc513 +#define USB_DEVICE_ID_DINOVO_DESKTOP 0xc704 +#define USB_DEVICE_ID_DINOVO_EDGE 0xc714 +#define USB_DEVICE_ID_DINOVO_MINI 0xc71f +#define USB_DEVICE_ID_LOGITECH_MOMO_WHEEL2 0xca03 + +#define USB_VENDOR_ID_MCC 0x09db +#define USB_DEVICE_ID_MCC_PMD1024LS 0x0076 +#define USB_DEVICE_ID_MCC_PMD1208LS 0x007a + +#define USB_VENDOR_ID_MGE 0x0463 +#define USB_DEVICE_ID_MGE_UPS 0xffff +#define USB_DEVICE_ID_MGE_UPS1 0x0001 + +#define USB_VENDOR_ID_MICROCHIP 0x04d8 +#define USB_DEVICE_ID_PICKIT1 0x0032 +#define USB_DEVICE_ID_PICKIT2 0x0033 + +#define USB_VENDOR_ID_MICROSOFT 0x045e +#define USB_DEVICE_ID_SIDEWINDER_GV 0x003b +#define USB_DEVICE_ID_WIRELESS_OPTICAL_DESKTOP_3_0 0x009d +#define USB_DEVICE_ID_MS_NE4K 0x00db +#define USB_DEVICE_ID_MS_LK6K 0x00f9 +#define USB_DEVICE_ID_MS_PRESENTER_8K_BT 0x0701 +#define USB_DEVICE_ID_MS_PRESENTER_8K_USB 0x0713 + + +#define USB_VENDOR_ID_MONTEREY 0x0566 +#define USB_DEVICE_ID_GENIUS_KB29E 0x3004 + +#define USB_VENDOR_ID_NCR 0x0404 +#define USB_DEVICE_ID_NCR_FIRST 0x0300 +#define USB_DEVICE_ID_NCR_LAST 0x03ff + +#define USB_VENDOR_ID_NATIONAL_SEMICONDUCTOR 0x0400 +#define USB_DEVICE_ID_N_S_HARMONY 0xc359 + +#define USB_VENDOR_ID_NATSU 0x08b7 +#define USB_DEVICE_ID_NATSU_GAMEPAD 0x0001 + +#define USB_VENDOR_ID_NEC 0x073e +#define USB_DEVICE_ID_NEC_USB_GAME_PAD 0x0301 + +#define USB_VENDOR_ID_ONTRAK 0x0a07 +#define USB_DEVICE_ID_ONTRAK_ADU100 0x0064 + +#define USB_VENDOR_ID_PANJIT 0x134c + +#define USB_VENDOR_ID_PANTHERLORD 0x0810 +#define USB_DEVICE_ID_PANTHERLORD_TWIN_USB_JOYSTICK 0x0001 + +#define USB_VENDOR_ID_PETALYNX 0x18b1 +#define USB_DEVICE_ID_PETALYNX_MAXTER_REMOTE 0x0037 + +#define USB_VENDOR_ID_PLAYDOTCOM 0x0b43 +#define USB_DEVICE_ID_PLAYDOTCOM_EMS_USBII 0x0003 + +#define USB_VENDOR_ID_SAITEK 0x06a3 +#define USB_DEVICE_ID_SAITEK_RUMBLEPAD 0xff17 + +#define USB_VENDOR_ID_SAMSUNG 0x0419 +#define USB_DEVICE_ID_SAMSUNG_IR_REMOTE 0x0001 + +#define USB_VENDOR_ID_SONY 0x054c +#define USB_DEVICE_ID_SONY_PS3_CONTROLLER 0x0268 + +#define USB_VENDOR_ID_SOUNDGRAPH 0x15c2 +#define USB_DEVICE_ID_SOUNDGRAPH_IMON_LCD 0x0038 + +#define USB_VENDOR_ID_SUN 0x0430 +#define USB_DEVICE_ID_RARITAN_KVM_DONGLE 0xcdab + +#define USB_VENDOR_ID_SUNPLUS 0x04fc +#define USB_DEVICE_ID_SUNPLUS_WDESKTOP 0x05d8 + +#define USB_VENDOR_ID_TENX 0x1130 +#define USB_DEVICE_ID_TENX_IBUDDY1 0x0001 +#define USB_DEVICE_ID_TENX_IBUDDY2 0x0002 + +#define USB_VENDOR_ID_THRUSTMASTER 0x044f + +#define USB_VENDOR_ID_TOPMAX 0x0663 +#define USB_DEVICE_ID_TOPMAX_COBRAPAD 0x0103 + +#define USB_VENDOR_ID_TURBOX 0x062a +#define USB_DEVICE_ID_TURBOX_KEYBOARD 0x0201 + +#define USB_VENDOR_ID_VERNIER 0x08f7 +#define USB_DEVICE_ID_VERNIER_LABPRO 0x0001 +#define USB_DEVICE_ID_VERNIER_GOTEMP 0x0002 +#define USB_DEVICE_ID_VERNIER_SKIP 0x0003 +#define USB_DEVICE_ID_VERNIER_CYCLOPS 0x0004 +#define USB_DEVICE_ID_VERNIER_LCSPEC 0x0006 + +#define USB_VENDOR_ID_WACOM 0x056a + +#define USB_VENDOR_ID_WISEGROUP 0x0925 +#define USB_DEVICE_ID_1_PHIDGETSERVO_20 0x8101 +#define USB_DEVICE_ID_4_PHIDGETSERVO_20 0x8104 +#define USB_DEVICE_ID_8_8_4_IF_KIT 0x8201 +#define USB_DEVICE_ID_QUAD_USB_JOYPAD 0x8800 +#define USB_DEVICE_ID_DUAL_USB_JOYPAD 0x8866 + +#define USB_VENDOR_ID_WISEGROUP_LTD 0x6666 +#define USB_VENDOR_ID_WISEGROUP_LTD2 0x6677 +#define USB_DEVICE_ID_SMARTJOY_DUAL_PLUS 0x8802 + +#define USB_VENDOR_ID_YEALINK 0x6993 +#define USB_DEVICE_ID_YEALINK_P1K_P4K_B2K 0xb001 + +#define USB_VENDOR_ID_ZEROPLUS 0x0c12 + +#define USB_VENDOR_ID_KYE 0x0458 +#define USB_DEVICE_ID_KYE_GPEN_560 0x5003 + +#endif diff --git a/drivers/hid/hid-input-quirks.c b/drivers/hid/hid-input-quirks.c deleted file mode 100644 index 16feea014494..000000000000 --- a/drivers/hid/hid-input-quirks.c +++ /dev/null @@ -1,484 +0,0 @@ -/* - * HID-input usage mapping quirks - * - * This is used to handle HID-input mappings for devices violating - * HUT 1.12 specification. - * - * Copyright (c) 2007-2008 Jiri Kosina - */ - -/* - * 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 - */ - -#include <linux/input.h> -#include <linux/hid.h> - -#define map_abs(c) do { usage->code = c; usage->type = EV_ABS; *bit = input->absbit; *max = ABS_MAX; } while (0) -#define map_rel(c) do { usage->code = c; usage->type = EV_REL; *bit = input->relbit; *max = REL_MAX; } while (0) -#define map_key(c) do { usage->code = c; usage->type = EV_KEY; *bit = input->keybit; *max = KEY_MAX; } while (0) -#define map_led(c) do { usage->code = c; usage->type = EV_LED; *bit = input->ledbit; *max = LED_MAX; } while (0) - -#define map_abs_clear(c) do { map_abs(c); clear_bit(c, *bit); } while (0) -#define map_key_clear(c) do { map_key(c); clear_bit(c, *bit); } while (0) - -static int quirk_belkin_wkbd(struct hid_usage *usage, struct input_dev *input, - unsigned long **bit, int *max) -{ - if ((usage->hid & HID_USAGE_PAGE) != HID_UP_CONSUMER) - return 0; - - switch (usage->hid & HID_USAGE) { - case 0x03a: map_key_clear(KEY_SOUND); break; - case 0x03b: map_key_clear(KEY_CAMERA); break; - case 0x03c: map_key_clear(KEY_DOCUMENTS); break; - default: - return 0; - } - return 1; -} - -static int quirk_cherry_cymotion(struct hid_usage *usage, struct input_dev *input, - unsigned long **bit, int *max) -{ - if ((usage->hid & HID_USAGE_PAGE) != HID_UP_CONSUMER) - return 0; - - switch (usage->hid & HID_USAGE) { - case 0x301: map_key_clear(KEY_PROG1); break; - case 0x302: map_key_clear(KEY_PROG2); break; - case 0x303: map_key_clear(KEY_PROG3); break; - default: - return 0; - } - return 1; -} - -static int quirk_logitech_ultrax_remote(struct hid_usage *usage, struct input_dev *input, - unsigned long **bit, int *max) -{ - if ((usage->hid & HID_USAGE_PAGE) != HID_UP_LOGIVENDOR) - return 0; - - set_bit(EV_REP, input->evbit); - switch(usage->hid & HID_USAGE) { - /* Reported on Logitech Ultra X Media Remote */ - case 0x004: map_key_clear(KEY_AGAIN); break; - case 0x00d: map_key_clear(KEY_HOME); break; - case 0x024: map_key_clear(KEY_SHUFFLE); break; - case 0x025: map_key_clear(KEY_TV); break; - case 0x026: map_key_clear(KEY_MENU); break; - case 0x031: map_key_clear(KEY_AUDIO); break; - case 0x032: map_key_clear(KEY_TEXT); break; - case 0x033: map_key_clear(KEY_LAST); break; - case 0x047: map_key_clear(KEY_MP3); break; - case 0x048: map_key_clear(KEY_DVD); break; - case 0x049: map_key_clear(KEY_MEDIA); break; - case 0x04a: map_key_clear(KEY_VIDEO); break; - case 0x04b: map_key_clear(KEY_ANGLE); break; - case 0x04c: map_key_clear(KEY_LANGUAGE); break; - case 0x04d: map_key_clear(KEY_SUBTITLE); break; - case 0x051: map_key_clear(KEY_RED); break; - case 0x052: map_key_clear(KEY_CLOSE); break; - - default: - return 0; - } - return 1; -} - -static int quirk_gyration_remote(struct hid_usage *usage, struct input_dev *input, - unsigned long **bit, int *max) -{ - if ((usage->hid & HID_USAGE_PAGE) != HID_UP_LOGIVENDOR) - return 0; - - set_bit(EV_REP, input->evbit); - switch(usage->hid & HID_USAGE) { - /* Reported on Gyration MCE Remote */ - case 0x00d: map_key_clear(KEY_HOME); break; - case 0x024: map_key_clear(KEY_DVD); break; - case 0x025: map_key_clear(KEY_PVR); break; - case 0x046: map_key_clear(KEY_MEDIA); break; - case 0x047: map_key_clear(KEY_MP3); break; - case 0x049: map_key_clear(KEY_CAMERA); break; - case 0x04a: map_key_clear(KEY_VIDEO); break; - - default: - return 0; - } - return 1; -} - -static int quirk_chicony_tactical_pad(struct hid_usage *usage, struct input_dev *input, - unsigned long **bit, int *max) -{ - if ((usage->hid & HID_USAGE_PAGE) != HID_UP_MSVENDOR) - return 0; - - set_bit(EV_REP, input->evbit); - switch (usage->hid & HID_USAGE) { - case 0xff01: map_key_clear(BTN_1); break; - case 0xff02: map_key_clear(BTN_2); break; - case 0xff03: map_key_clear(BTN_3); break; - case 0xff04: map_key_clear(BTN_4); break; - case 0xff05: map_key_clear(BTN_5); break; - case 0xff06: map_key_clear(BTN_6); break; - case 0xff07: map_key_clear(BTN_7); break; - case 0xff08: map_key_clear(BTN_8); break; - case 0xff09: map_key_clear(BTN_9); break; - case 0xff0a: map_key_clear(BTN_A); break; - case 0xff0b: map_key_clear(BTN_B); break; - default: - return 0; - } - return 1; -} - -static int quirk_microsoft_ergonomy_kb(struct hid_usage *usage, struct input_dev *input, - unsigned long **bit, int *max) -{ - if ((usage->hid & HID_USAGE_PAGE) != HID_UP_MSVENDOR) - return 0; - - switch(usage->hid & HID_USAGE) { - case 0xfd06: map_key_clear(KEY_CHAT); break; - case 0xfd07: map_key_clear(KEY_PHONE); break; - case 0xff05: - set_bit(EV_REP, input->evbit); - map_key_clear(KEY_F13); - set_bit(KEY_F14, input->keybit); - set_bit(KEY_F15, input->keybit); - set_bit(KEY_F16, input->keybit); - set_bit(KEY_F17, input->keybit); - set_bit(KEY_F18, input->keybit); - default: - return 0; - } - return 1; -} - -static int quirk_microsoft_presenter_8k(struct hid_usage *usage, struct input_dev *input, - unsigned long **bit, int *max) -{ - if ((usage->hid & HID_USAGE_PAGE) != HID_UP_MSVENDOR) - return 0; - - set_bit(EV_REP, input->evbit); - switch(usage->hid & HID_USAGE) { - case 0xfd08: map_key_clear(KEY_FORWARD); break; - case 0xfd09: map_key_clear(KEY_BACK); break; - case 0xfd0b: map_key_clear(KEY_PLAYPAUSE); break; - case 0xfd0e: map_key_clear(KEY_CLOSE); break; - case 0xfd0f: map_key_clear(KEY_PLAY); break; - default: - return 0; - } - return 1; -} - -static int quirk_petalynx_remote(struct hid_usage *usage, struct input_dev *input, - unsigned long **bit, int *max) -{ - if (((usage->hid & HID_USAGE_PAGE) != HID_UP_LOGIVENDOR) && - ((usage->hid & HID_USAGE_PAGE) != HID_UP_CONSUMER)) - return 0; - - if ((usage->hid & HID_USAGE_PAGE) == HID_UP_LOGIVENDOR) - switch(usage->hid & HID_USAGE) { - case 0x05a: map_key_clear(KEY_TEXT); break; - case 0x05b: map_key_clear(KEY_RED); break; - case 0x05c: map_key_clear(KEY_GREEN); break; - case 0x05d: map_key_clear(KEY_YELLOW); break; - case 0x05e: map_key_clear(KEY_BLUE); break; - default: - return 0; - } - - if ((usage->hid & HID_USAGE_PAGE) == HID_UP_CONSUMER) - switch(usage->hid & HID_USAGE) { - case 0x0f6: map_key_clear(KEY_NEXT); break; - case 0x0fa: map_key_clear(KEY_BACK); break; - default: - return 0; - } - return 1; -} - -static int quirk_logitech_wireless(struct hid_usage *usage, struct input_dev *input, - unsigned long **bit, int *max) -{ - if ((usage->hid & HID_USAGE_PAGE) != HID_UP_CONSUMER) - return 0; - - switch (usage->hid & HID_USAGE) { - case 0x1001: map_key_clear(KEY_MESSENGER); break; - case 0x1003: map_key_clear(KEY_SOUND); break; - case 0x1004: map_key_clear(KEY_VIDEO); break; - case 0x1005: map_key_clear(KEY_AUDIO); break; - case 0x100a: map_key_clear(KEY_DOCUMENTS); break; - case 0x1011: map_key_clear(KEY_PREVIOUSSONG); break; - case 0x1012: map_key_clear(KEY_NEXTSONG); break; - case 0x1013: map_key_clear(KEY_CAMERA); break; - case 0x1014: map_key_clear(KEY_MESSENGER); break; - case 0x1015: map_key_clear(KEY_RECORD); break; - case 0x1016: map_key_clear(KEY_PLAYER); break; - case 0x1017: map_key_clear(KEY_EJECTCD); break; - case 0x1018: map_key_clear(KEY_MEDIA); break; - case 0x1019: map_key_clear(KEY_PROG1); break; - case 0x101a: map_key_clear(KEY_PROG2); break; - case 0x101b: map_key_clear(KEY_PROG3); break; - case 0x101f: map_key_clear(KEY_ZOOMIN); break; - case 0x1020: map_key_clear(KEY_ZOOMOUT); break; - case 0x1021: map_key_clear(KEY_ZOOMRESET); break; - case 0x1023: map_key_clear(KEY_CLOSE); break; - case 0x1027: map_key_clear(KEY_MENU); break; - /* this one is marked as 'Rotate' */ - case 0x1028: map_key_clear(KEY_ANGLE); break; - case 0x1029: map_key_clear(KEY_SHUFFLE); break; - case 0x102a: map_key_clear(KEY_BACK); break; - case 0x102b: map_key_clear(KEY_CYCLEWINDOWS); break; - case 0x1041: map_key_clear(KEY_BATTERY); break; - case 0x1042: map_key_clear(KEY_WORDPROCESSOR); break; - case 0x1043: map_key_clear(KEY_SPREADSHEET); break; - case 0x1044: map_key_clear(KEY_PRESENTATION); break; - case 0x1045: map_key_clear(KEY_UNDO); break; - case 0x1046: map_key_clear(KEY_REDO); break; - case 0x1047: map_key_clear(KEY_PRINT); break; - case 0x1048: map_key_clear(KEY_SAVE); break; - case 0x1049: map_key_clear(KEY_PROG1); break; - case 0x104a: map_key_clear(KEY_PROG2); break; - case 0x104b: map_key_clear(KEY_PROG3); break; - case 0x104c: map_key_clear(KEY_PROG4); break; - - default: - return 0; - } - return 1; -} - -static int quirk_cherry_genius_29e(struct hid_usage *usage, struct input_dev *input, - unsigned long **bit, int *max) -{ - if ((usage->hid & HID_USAGE_PAGE) != HID_UP_CONSUMER) - return 0; - - switch (usage->hid & HID_USAGE) { - case 0x156: map_key_clear(KEY_WORDPROCESSOR); break; - case 0x157: map_key_clear(KEY_SPREADSHEET); break; - case 0x158: map_key_clear(KEY_PRESENTATION); break; - case 0x15c: map_key_clear(KEY_STOP); break; - - default: - return 0; - } - return 1; -} - -static int quirk_btc_8193(struct hid_usage *usage, struct input_dev *input, - unsigned long **bit, int *max) -{ - if ((usage->hid & HID_USAGE_PAGE) != HID_UP_CONSUMER) - return 0; - - switch (usage->hid & HID_USAGE) { - case 0x230: map_key(BTN_MOUSE); break; - case 0x231: map_rel(REL_WHEEL); break; - /* - * this keyboard has a scrollwheel implemented in - * totally broken way. We map this usage temporarily - * to HWHEEL and handle it in the event quirk handler - */ - case 0x232: map_rel(REL_HWHEEL); break; - - default: - return 0; - } - return 1; -} - -static int quirk_sunplus_wdesktop(struct hid_usage *usage, struct input_dev *input, - unsigned long **bit, int *max) -{ - if ((usage->hid & HID_USAGE_PAGE) != HID_UP_CONSUMER) - return 0; - - switch (usage->hid & HID_USAGE) { - case 0x2003: map_key_clear(KEY_ZOOMIN); break; - case 0x2103: map_key_clear(KEY_ZOOMOUT); break; - default: - return 0; - } - return 1; -} - -#define VENDOR_ID_BELKIN 0x1020 -#define DEVICE_ID_BELKIN_WIRELESS_KEYBOARD 0x0006 - -#define VENDOR_ID_CHERRY 0x046a -#define DEVICE_ID_CHERRY_CYMOTION 0x0023 - -#define VENDOR_ID_CHICONY 0x04f2 -#define DEVICE_ID_CHICONY_TACTICAL_PAD 0x0418 - -#define VENDOR_ID_EZKEY 0x0518 -#define DEVICE_ID_BTC_8193 0x0002 - -#define VENDOR_ID_GYRATION 0x0c16 -#define DEVICE_ID_GYRATION_REMOTE 0x0002 - -#define VENDOR_ID_LOGITECH 0x046d -#define DEVICE_ID_LOGITECH_RECEIVER 0xc101 -#define DEVICE_ID_S510_RECEIVER 0xc50c -#define DEVICE_ID_S510_RECEIVER_2 0xc517 -#define DEVICE_ID_MX3000_RECEIVER 0xc513 - -#define VENDOR_ID_MICROSOFT 0x045e -#define DEVICE_ID_MS4K 0x00db -#define DEVICE_ID_MS6K 0x00f9 -#define DEVICE_IS_MS_PRESENTER_8K_BT 0x0701 -#define DEVICE_ID_MS_PRESENTER_8K_USB 0x0713 - -#define VENDOR_ID_MONTEREY 0x0566 -#define DEVICE_ID_GENIUS_KB29E 0x3004 - -#define VENDOR_ID_PETALYNX 0x18b1 -#define DEVICE_ID_PETALYNX_MAXTER_REMOTE 0x0037 - -#define VENDOR_ID_SUNPLUS 0x04fc -#define DEVICE_ID_SUNPLUS_WDESKTOP 0x05d8 - -static const struct hid_input_blacklist { - __u16 idVendor; - __u16 idProduct; - int (*quirk)(struct hid_usage *, struct input_dev *, unsigned long **, int *); -} hid_input_blacklist[] = { - { VENDOR_ID_BELKIN, DEVICE_ID_BELKIN_WIRELESS_KEYBOARD, quirk_belkin_wkbd }, - - { VENDOR_ID_CHERRY, DEVICE_ID_CHERRY_CYMOTION, quirk_cherry_cymotion }, - - { VENDOR_ID_CHICONY, DEVICE_ID_CHICONY_TACTICAL_PAD, quirk_chicony_tactical_pad }, - - { VENDOR_ID_EZKEY, DEVICE_ID_BTC_8193, quirk_btc_8193 }, - - { VENDOR_ID_GYRATION, DEVICE_ID_GYRATION_REMOTE, quirk_gyration_remote }, - - { VENDOR_ID_LOGITECH, DEVICE_ID_LOGITECH_RECEIVER, quirk_logitech_ultrax_remote }, - { VENDOR_ID_LOGITECH, DEVICE_ID_S510_RECEIVER, quirk_logitech_wireless }, - { VENDOR_ID_LOGITECH, DEVICE_ID_S510_RECEIVER_2, quirk_logitech_wireless }, - { VENDOR_ID_LOGITECH, DEVICE_ID_MX3000_RECEIVER, quirk_logitech_wireless }, - - { VENDOR_ID_MICROSOFT, DEVICE_ID_MS4K, quirk_microsoft_ergonomy_kb }, - { VENDOR_ID_MICROSOFT, DEVICE_ID_MS6K, quirk_microsoft_ergonomy_kb }, - { VENDOR_ID_MICROSOFT, DEVICE_IS_MS_PRESENTER_8K_BT, quirk_microsoft_presenter_8k }, - { VENDOR_ID_MICROSOFT, DEVICE_ID_MS_PRESENTER_8K_USB, quirk_microsoft_presenter_8k }, - - { VENDOR_ID_MONTEREY, DEVICE_ID_GENIUS_KB29E, quirk_cherry_genius_29e }, - - { VENDOR_ID_PETALYNX, DEVICE_ID_PETALYNX_MAXTER_REMOTE, quirk_petalynx_remote }, - - { VENDOR_ID_SUNPLUS, DEVICE_ID_SUNPLUS_WDESKTOP, quirk_sunplus_wdesktop }, - - { 0, 0, NULL } -}; - -int hidinput_mapping_quirks(struct hid_usage *usage, - struct input_dev *input, - unsigned long **bit, int *max) -{ - struct hid_device *device = input_get_drvdata(input); - int i = 0; - - while (hid_input_blacklist[i].quirk) { - if (hid_input_blacklist[i].idVendor == device->vendor && - hid_input_blacklist[i].idProduct == device->product) - return hid_input_blacklist[i].quirk(usage, input, bit, max); - i++; - } - return 0; -} - -int hidinput_event_quirks(struct hid_device *hid, struct hid_field *field, struct hid_usage *usage, __s32 value) -{ - struct input_dev *input; - - input = field->hidinput->input; - - if (((hid->quirks & HID_QUIRK_2WHEEL_MOUSE_HACK_5) && (usage->hid == 0x00090005)) - || ((hid->quirks & HID_QUIRK_2WHEEL_MOUSE_HACK_7) && (usage->hid == 0x00090007))) { - if (value) hid->quirks |= HID_QUIRK_2WHEEL_MOUSE_HACK_ON; - else hid->quirks &= ~HID_QUIRK_2WHEEL_MOUSE_HACK_ON; - return 1; - } - - if ((hid->quirks & HID_QUIRK_2WHEEL_MOUSE_HACK_B8) && - (usage->type == EV_REL) && - (usage->code == REL_WHEEL)) { - hid->delayed_value = value; - return 1; - } - - if ((hid->quirks & HID_QUIRK_2WHEEL_MOUSE_HACK_B8) && - (usage->hid == 0x000100b8)) { - input_event(input, EV_REL, value ? REL_HWHEEL : REL_WHEEL, hid->delayed_value); - return 1; - } - - if ((hid->quirks & HID_QUIRK_INVERT_HWHEEL) && (usage->code == REL_HWHEEL)) { - input_event(input, usage->type, usage->code, -value); - return 1; - } - - if ((hid->quirks & HID_QUIRK_2WHEEL_MOUSE_HACK_ON) && (usage->code == REL_WHEEL)) { - input_event(input, usage->type, REL_HWHEEL, value); - return 1; - } - - if ((hid->quirks & HID_QUIRK_APPLE_HAS_FN) && hidinput_apple_event(hid, input, usage, value)) - return 1; - - /* Handling MS keyboards special buttons */ - if (hid->quirks & HID_QUIRK_MICROSOFT_KEYS && - usage->hid == (HID_UP_MSVENDOR | 0xff05)) { - int key = 0; - static int last_key = 0; - switch (value) { - case 0x01: key = KEY_F14; break; - case 0x02: key = KEY_F15; break; - case 0x04: key = KEY_F16; break; - case 0x08: key = KEY_F17; break; - case 0x10: key = KEY_F18; break; - default: break; - } - if (key) { - input_event(input, usage->type, key, 1); - last_key = key; - } else { - input_event(input, usage->type, last_key, 0); - } - } - - /* handle the temporary quirky mapping to HWHEEL */ - if (hid->quirks & HID_QUIRK_HWHEEL_WHEEL_INVERT && - usage->type == EV_REL && usage->code == REL_HWHEEL) { - input_event(input, usage->type, REL_WHEEL, -value); - return 1; - } - - /* Gyration MCE remote "Sleep" key */ - if (hid->vendor == VENDOR_ID_GYRATION && - hid->product == DEVICE_ID_GYRATION_REMOTE && - (usage->hid & HID_USAGE_PAGE) == HID_UP_GENDESK && - (usage->hid & 0xff) == 0x82) { - input_event(input, usage->type, usage->code, 1); - input_sync(input); - input_event(input, usage->type, usage->code, 0); - input_sync(input); - return 1; - } - return 0; -} - - diff --git a/drivers/hid/hid-input.c b/drivers/hid/hid-input.c index 1b2e8dc3398d..7f183b7147e1 100644 --- a/drivers/hid/hid-input.c +++ b/drivers/hid/hid-input.c @@ -32,11 +32,6 @@ #include <linux/hid.h> #include <linux/hid-debug.h> -static int hid_apple_fnmode = 1; -module_param_named(pb_fnmode, hid_apple_fnmode, int, 0644); -MODULE_PARM_DESC(pb_fnmode, - "Mode of fn key on Apple keyboards (0 = disabled, 1 = fkeyslast, 2 = fkeysfirst)"); - #define unk KEY_UNKNOWN static const unsigned char hid_keyboard[256] = { @@ -58,227 +53,20 @@ static const unsigned char hid_keyboard[256] = { 150,158,159,128,136,177,178,176,142,152,173,140,unk,unk,unk,unk }; -/* extended mapping for certain Logitech hardware (Logitech cordless desktop LX500) */ -#define LOGITECH_EXPANDED_KEYMAP_SIZE 80 -static int logitech_expanded_keymap[LOGITECH_EXPANDED_KEYMAP_SIZE] = { - 0,216, 0,213,175,156, 0, 0, 0, 0, - 144, 0, 0, 0, 0, 0, 0, 0, 0,212, - 174,167,152,161,112, 0, 0, 0,154, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0,183,184,185,186,187, - 188,189,190,191,192,193,194, 0, 0, 0 -}; - static const struct { __s32 x; __s32 y; } hid_hat_to_axis[] = {{ 0, 0}, { 0,-1}, { 1,-1}, { 1, 0}, { 1, 1}, { 0, 1}, {-1, 1}, {-1, 0}, {-1,-1}}; -#define map_abs(c) do { usage->code = c; usage->type = EV_ABS; bit = input->absbit; max = ABS_MAX; } while (0) -#define map_rel(c) do { usage->code = c; usage->type = EV_REL; bit = input->relbit; max = REL_MAX; } while (0) -#define map_key(c) do { usage->code = c; usage->type = EV_KEY; bit = input->keybit; max = KEY_MAX; } while (0) -#define map_led(c) do { usage->code = c; usage->type = EV_LED; bit = input->ledbit; max = LED_MAX; } while (0) - -#define map_abs_clear(c) do { map_abs(c); clear_bit(c, bit); } while (0) -#define map_key_clear(c) do { map_key(c); clear_bit(c, bit); } while (0) - -#ifdef CONFIG_USB_HIDINPUT_POWERBOOK - -struct hidinput_key_translation { - u16 from; - u16 to; - u8 flags; -}; - -#define APPLE_FLAG_FKEY 0x01 - -static struct hidinput_key_translation apple_fn_keys[] = { - { KEY_BACKSPACE, KEY_DELETE }, - { KEY_F1, KEY_BRIGHTNESSDOWN, APPLE_FLAG_FKEY }, - { KEY_F2, KEY_BRIGHTNESSUP, APPLE_FLAG_FKEY }, - { KEY_F3, KEY_FN_F5, APPLE_FLAG_FKEY }, /* Exposé */ - { KEY_F4, KEY_FN_F4, APPLE_FLAG_FKEY }, /* Dashboard */ - { KEY_F5, KEY_KBDILLUMDOWN, APPLE_FLAG_FKEY }, - { KEY_F6, KEY_KBDILLUMUP, APPLE_FLAG_FKEY }, - { KEY_F7, KEY_PREVIOUSSONG, APPLE_FLAG_FKEY }, - { KEY_F8, KEY_PLAYPAUSE, APPLE_FLAG_FKEY }, - { KEY_F9, KEY_NEXTSONG, APPLE_FLAG_FKEY }, - { KEY_F10, KEY_MUTE, APPLE_FLAG_FKEY }, - { KEY_F11, KEY_VOLUMEDOWN, APPLE_FLAG_FKEY }, - { KEY_F12, KEY_VOLUMEUP, APPLE_FLAG_FKEY }, - { KEY_UP, KEY_PAGEUP }, - { KEY_DOWN, KEY_PAGEDOWN }, - { KEY_LEFT, KEY_HOME }, - { KEY_RIGHT, KEY_END }, - { } -}; - -static struct hidinput_key_translation powerbook_fn_keys[] = { - { KEY_BACKSPACE, KEY_DELETE }, - { KEY_F1, KEY_BRIGHTNESSDOWN, APPLE_FLAG_FKEY }, - { KEY_F2, KEY_BRIGHTNESSUP, APPLE_FLAG_FKEY }, - { KEY_F3, KEY_MUTE, APPLE_FLAG_FKEY }, - { KEY_F4, KEY_VOLUMEDOWN, APPLE_FLAG_FKEY }, - { KEY_F5, KEY_VOLUMEUP, APPLE_FLAG_FKEY }, - { KEY_F6, KEY_NUMLOCK, APPLE_FLAG_FKEY }, - { KEY_F7, KEY_SWITCHVIDEOMODE, APPLE_FLAG_FKEY }, - { KEY_F8, KEY_KBDILLUMTOGGLE, APPLE_FLAG_FKEY }, - { KEY_F9, KEY_KBDILLUMDOWN, APPLE_FLAG_FKEY }, - { KEY_F10, KEY_KBDILLUMUP, APPLE_FLAG_FKEY }, - { KEY_UP, KEY_PAGEUP }, - { KEY_DOWN, KEY_PAGEDOWN }, - { KEY_LEFT, KEY_HOME }, - { KEY_RIGHT, KEY_END }, - { } -}; - -static struct hidinput_key_translation powerbook_numlock_keys[] = { - { KEY_J, KEY_KP1 }, - { KEY_K, KEY_KP2 }, - { KEY_L, KEY_KP3 }, - { KEY_U, KEY_KP4 }, - { KEY_I, KEY_KP5 }, - { KEY_O, KEY_KP6 }, - { KEY_7, KEY_KP7 }, - { KEY_8, KEY_KP8 }, - { KEY_9, KEY_KP9 }, - { KEY_M, KEY_KP0 }, - { KEY_DOT, KEY_KPDOT }, - { KEY_SLASH, KEY_KPPLUS }, - { KEY_SEMICOLON, KEY_KPMINUS }, - { KEY_P, KEY_KPASTERISK }, - { KEY_MINUS, KEY_KPEQUAL }, - { KEY_0, KEY_KPSLASH }, - { KEY_F6, KEY_NUMLOCK }, - { KEY_KPENTER, KEY_KPENTER }, - { KEY_BACKSPACE, KEY_BACKSPACE }, - { } -}; - -static struct hidinput_key_translation apple_iso_keyboard[] = { - { KEY_GRAVE, KEY_102ND }, - { KEY_102ND, KEY_GRAVE }, - { } -}; +#define map_abs(c) hid_map_usage(hidinput, usage, &bit, &max, EV_ABS, (c)) +#define map_rel(c) hid_map_usage(hidinput, usage, &bit, &max, EV_REL, (c)) +#define map_key(c) hid_map_usage(hidinput, usage, &bit, &max, EV_KEY, (c)) +#define map_led(c) hid_map_usage(hidinput, usage, &bit, &max, EV_LED, (c)) -static struct hidinput_key_translation *find_translation(struct hidinput_key_translation *table, u16 from) -{ - struct hidinput_key_translation *trans; - - /* Look for the translation */ - for (trans = table; trans->from; trans++) - if (trans->from == from) - return trans; - - return NULL; -} - -int hidinput_apple_event(struct hid_device *hid, struct input_dev *input, - struct hid_usage *usage, __s32 value) -{ - struct hidinput_key_translation *trans; - - if (usage->code == KEY_FN) { - if (value) hid->quirks |= HID_QUIRK_APPLE_FN_ON; - else hid->quirks &= ~HID_QUIRK_APPLE_FN_ON; - - input_event(input, usage->type, usage->code, value); - - return 1; - } - - if (hid_apple_fnmode) { - int do_translate; - - trans = find_translation((hid->product < 0x220 || - hid->product >= 0x300) ? - powerbook_fn_keys : apple_fn_keys, - usage->code); - if (trans) { - if (test_bit(usage->code, hid->apple_pressed_fn)) - do_translate = 1; - else if (trans->flags & APPLE_FLAG_FKEY) - do_translate = - (hid_apple_fnmode == 2 && (hid->quirks & HID_QUIRK_APPLE_FN_ON)) || - (hid_apple_fnmode == 1 && !(hid->quirks & HID_QUIRK_APPLE_FN_ON)); - else - do_translate = (hid->quirks & HID_QUIRK_APPLE_FN_ON); - - if (do_translate) { - if (value) - set_bit(usage->code, hid->apple_pressed_fn); - else - clear_bit(usage->code, hid->apple_pressed_fn); - - input_event(input, usage->type, trans->to, value); - - return 1; - } - } - - if (hid->quirks & HID_QUIRK_APPLE_NUMLOCK_EMULATION && ( - test_bit(usage->code, hid->pb_pressed_numlock) || - test_bit(LED_NUML, input->led))) { - trans = find_translation(powerbook_numlock_keys, usage->code); - - if (trans) { - if (value) - set_bit(usage->code, hid->pb_pressed_numlock); - else - clear_bit(usage->code, hid->pb_pressed_numlock); - - input_event(input, usage->type, trans->to, value); - } - - return 1; - } - } - - if (hid->quirks & HID_QUIRK_APPLE_ISO_KEYBOARD) { - trans = find_translation(apple_iso_keyboard, usage->code); - if (trans) { - input_event(input, usage->type, trans->to, value); - return 1; - } - } - - return 0; -} - -static void hidinput_apple_setup(struct input_dev *input) -{ - struct hidinput_key_translation *trans; - - set_bit(KEY_NUMLOCK, input->keybit); - - /* Enable all needed keys */ - for (trans = apple_fn_keys; trans->from; trans++) - set_bit(trans->to, input->keybit); - - for (trans = powerbook_fn_keys; trans->from; trans++) - set_bit(trans->to, input->keybit); - - for (trans = powerbook_numlock_keys; trans->from; trans++) - set_bit(trans->to, input->keybit); - - for (trans = apple_iso_keyboard; trans->from; trans++) - set_bit(trans->to, input->keybit); - -} -#else -inline int hidinput_apple_event(struct hid_device *hid, - struct input_dev *input, - struct hid_usage *usage, __s32 value) -{ - return 0; -} - -static inline void hidinput_apple_setup(struct input_dev *input) -{ -} -#endif +#define map_abs_clear(c) hid_map_usage_clear(hidinput, usage, &bit, \ + &max, EV_ABS, (c)) +#define map_key_clear(c) hid_map_usage_clear(hidinput, usage, &bit, \ + &max, EV_KEY, (c)) static inline int match_scancode(int code, int scancode) { @@ -366,7 +154,7 @@ static void hidinput_configure_usage(struct hid_input *hidinput, struct hid_fiel { struct input_dev *input = hidinput->input; struct hid_device *device = input_get_drvdata(input); - int max = 0, code, ret; + int max = 0, code; unsigned long *bit = NULL; field->hidinput = hidinput; @@ -385,406 +173,345 @@ static void hidinput_configure_usage(struct hid_input *hidinput, struct hid_fiel goto ignore; } - /* handle input mappings for quirky devices */ - ret = hidinput_mapping_quirks(usage, input, &bit, &max); - if (ret) - goto mapped; + if (device->driver->input_mapping) { + int ret = device->driver->input_mapping(device, hidinput, field, + usage, &bit, &max); + if (ret > 0) + goto mapped; + if (ret < 0) + goto ignore; + } switch (usage->hid & HID_USAGE_PAGE) { + case HID_UP_UNDEFINED: + goto ignore; - case HID_UP_UNDEFINED: - goto ignore; - - case HID_UP_KEYBOARD: + case HID_UP_KEYBOARD: + set_bit(EV_REP, input->evbit); - set_bit(EV_REP, input->evbit); + if ((usage->hid & HID_USAGE) < 256) { + if (!hid_keyboard[usage->hid & HID_USAGE]) goto ignore; + map_key_clear(hid_keyboard[usage->hid & HID_USAGE]); + } else + map_key(KEY_UNKNOWN); - if ((usage->hid & HID_USAGE) < 256) { - if (!hid_keyboard[usage->hid & HID_USAGE]) goto ignore; - map_key_clear(hid_keyboard[usage->hid & HID_USAGE]); - } else - map_key(KEY_UNKNOWN); + break; - break; + case HID_UP_BUTTON: + code = ((usage->hid - 1) & 0xf); - case HID_UP_BUTTON: - - code = ((usage->hid - 1) & 0xf); - - switch (field->application) { - case HID_GD_MOUSE: - case HID_GD_POINTER: code += 0x110; break; - case HID_GD_JOYSTICK: code += 0x120; break; - case HID_GD_GAMEPAD: code += 0x130; break; - default: - switch (field->physical) { - case HID_GD_MOUSE: - case HID_GD_POINTER: code += 0x110; break; - case HID_GD_JOYSTICK: code += 0x120; break; - case HID_GD_GAMEPAD: code += 0x130; break; - default: code += 0x100; - } - } - - /* Special handling for Logitech Cordless Desktop */ - if (field->application != HID_GD_MOUSE) { - if (device->quirks & HID_QUIRK_LOGITECH_EXPANDED_KEYMAP) { - int hid = usage->hid & HID_USAGE; - if (hid < LOGITECH_EXPANDED_KEYMAP_SIZE && logitech_expanded_keymap[hid] != 0) - code = logitech_expanded_keymap[hid]; - } - } else { - if (device->quirks & HID_QUIRK_LOGITECH_IGNORE_DOUBLED_WHEEL) { - int hid = usage->hid & HID_USAGE; - if (hid == 7 || hid == 8) - goto ignore; - } + switch (field->application) { + case HID_GD_MOUSE: + case HID_GD_POINTER: code += 0x110; break; + case HID_GD_JOYSTICK: code += 0x120; break; + case HID_GD_GAMEPAD: code += 0x130; break; + default: + switch (field->physical) { + case HID_GD_MOUSE: + case HID_GD_POINTER: code += 0x110; break; + case HID_GD_JOYSTICK: code += 0x120; break; + case HID_GD_GAMEPAD: code += 0x130; break; + default: code += 0x100; } + } - map_key(code); - break; - - - case HID_UP_SIMULATION: - - switch (usage->hid & 0xffff) { - case 0xba: map_abs(ABS_RUDDER); break; - case 0xbb: map_abs(ABS_THROTTLE); break; - case 0xc4: map_abs(ABS_GAS); break; - case 0xc5: map_abs(ABS_BRAKE); break; - case 0xc8: map_abs(ABS_WHEEL); break; - default: goto ignore; + map_key(code); + break; + + case HID_UP_SIMULATION: + switch (usage->hid & 0xffff) { + case 0xba: map_abs(ABS_RUDDER); break; + case 0xbb: map_abs(ABS_THROTTLE); break; + case 0xc4: map_abs(ABS_GAS); break; + case 0xc5: map_abs(ABS_BRAKE); break; + case 0xc8: map_abs(ABS_WHEEL); break; + default: goto ignore; + } + break; + + case HID_UP_GENDESK: + if ((usage->hid & 0xf0) == 0x80) { /* SystemControl */ + switch (usage->hid & 0xf) { + case 0x1: map_key_clear(KEY_POWER); break; + case 0x2: map_key_clear(KEY_SLEEP); break; + case 0x3: map_key_clear(KEY_WAKEUP); break; + default: goto unknown; } break; + } - case HID_UP_GENDESK: - - if ((usage->hid & 0xf0) == 0x80) { /* SystemControl */ - switch (usage->hid & 0xf) { - case 0x1: map_key_clear(KEY_POWER); break; - case 0x2: map_key_clear(KEY_SLEEP); break; - case 0x3: map_key_clear(KEY_WAKEUP); break; - default: goto unknown; - } - break; - } - - if ((usage->hid & 0xf0) == 0x90) { /* D-pad */ - switch (usage->hid) { - case HID_GD_UP: usage->hat_dir = 1; break; - case HID_GD_DOWN: usage->hat_dir = 5; break; - case HID_GD_RIGHT: usage->hat_dir = 3; break; - case HID_GD_LEFT: usage->hat_dir = 7; break; - default: goto unknown; - } - if (field->dpad) { - map_abs(field->dpad); - goto ignore; - } - map_abs(ABS_HAT0X); - break; - } - + if ((usage->hid & 0xf0) == 0x90) { /* D-pad */ switch (usage->hid) { - - /* These usage IDs map directly to the usage codes. */ - case HID_GD_X: case HID_GD_Y: case HID_GD_Z: - case HID_GD_RX: case HID_GD_RY: case HID_GD_RZ: - case HID_GD_SLIDER: case HID_GD_DIAL: case HID_GD_WHEEL: - if (field->flags & HID_MAIN_ITEM_RELATIVE) - map_rel(usage->hid & 0xf); - else - map_abs(usage->hid & 0xf); - break; - - case HID_GD_HATSWITCH: - usage->hat_min = field->logical_minimum; - usage->hat_max = field->logical_maximum; - map_abs(ABS_HAT0X); - break; - - case HID_GD_START: map_key_clear(BTN_START); break; - case HID_GD_SELECT: map_key_clear(BTN_SELECT); break; - - default: goto unknown; + case HID_GD_UP: usage->hat_dir = 1; break; + case HID_GD_DOWN: usage->hat_dir = 5; break; + case HID_GD_RIGHT: usage->hat_dir = 3; break; + case HID_GD_LEFT: usage->hat_dir = 7; break; + default: goto unknown; } - - break; - - case HID_UP_LED: - - switch (usage->hid & 0xffff) { /* HID-Value: */ - case 0x01: map_led (LED_NUML); break; /* "Num Lock" */ - case 0x02: map_led (LED_CAPSL); break; /* "Caps Lock" */ - case 0x03: map_led (LED_SCROLLL); break; /* "Scroll Lock" */ - case 0x04: map_led (LED_COMPOSE); break; /* "Compose" */ - case 0x05: map_led (LED_KANA); break; /* "Kana" */ - case 0x27: map_led (LED_SLEEP); break; /* "Stand-By" */ - case 0x4c: map_led (LED_SUSPEND); break; /* "System Suspend" */ - case 0x09: map_led (LED_MUTE); break; /* "Mute" */ - case 0x4b: map_led (LED_MISC); break; /* "Generic Indicator" */ - case 0x19: map_led (LED_MAIL); break; /* "Message Waiting" */ - case 0x4d: map_led (LED_CHARGING); break; /* "External Power Connected" */ - - default: goto ignore; + if (field->dpad) { + map_abs(field->dpad); + goto ignore; } + map_abs(ABS_HAT0X); break; + } - case HID_UP_DIGITIZER: - - switch (usage->hid & 0xff) { - - case 0x30: /* TipPressure */ - if (!test_bit(BTN_TOUCH, input->keybit)) { - device->quirks |= HID_QUIRK_NOTOUCH; - set_bit(EV_KEY, input->evbit); - set_bit(BTN_TOUCH, input->keybit); - } - - map_abs_clear(ABS_PRESSURE); - break; - - case 0x32: /* InRange */ - switch (field->physical & 0xff) { - case 0x21: map_key(BTN_TOOL_MOUSE); break; - case 0x22: map_key(BTN_TOOL_FINGER); break; - default: map_key(BTN_TOOL_PEN); break; - } - break; + switch (usage->hid) { + /* These usage IDs map directly to the usage codes. */ + case HID_GD_X: case HID_GD_Y: case HID_GD_Z: + case HID_GD_RX: case HID_GD_RY: case HID_GD_RZ: + case HID_GD_SLIDER: case HID_GD_DIAL: case HID_GD_WHEEL: + if (field->flags & HID_MAIN_ITEM_RELATIVE) + map_rel(usage->hid & 0xf); + else + map_abs(usage->hid & 0xf); + break; - case 0x3c: /* Invert */ - map_key_clear(BTN_TOOL_RUBBER); - break; + case HID_GD_HATSWITCH: + usage->hat_min = field->logical_minimum; + usage->hat_max = field->logical_maximum; + map_abs(ABS_HAT0X); + break; - case 0x33: /* Touch */ - case 0x42: /* TipSwitch */ - case 0x43: /* TipSwitch2 */ - device->quirks &= ~HID_QUIRK_NOTOUCH; - map_key_clear(BTN_TOUCH); - break; + case HID_GD_START: map_key_clear(BTN_START); break; + case HID_GD_SELECT: map_key_clear(BTN_SELECT); break; - case 0x44: /* BarrelSwitch */ - map_key_clear(BTN_STYLUS); - break; + default: goto unknown; + } - default: goto unknown; + break; + + case HID_UP_LED: + switch (usage->hid & 0xffff) { /* HID-Value: */ + case 0x01: map_led (LED_NUML); break; /* "Num Lock" */ + case 0x02: map_led (LED_CAPSL); break; /* "Caps Lock" */ + case 0x03: map_led (LED_SCROLLL); break; /* "Scroll Lock" */ + case 0x04: map_led (LED_COMPOSE); break; /* "Compose" */ + case 0x05: map_led (LED_KANA); break; /* "Kana" */ + case 0x27: map_led (LED_SLEEP); break; /* "Stand-By" */ + case 0x4c: map_led (LED_SUSPEND); break; /* "System Suspend" */ + case 0x09: map_led (LED_MUTE); break; /* "Mute" */ + case 0x4b: map_led (LED_MISC); break; /* "Generic Indicator" */ + case 0x19: map_led (LED_MAIL); break; /* "Message Waiting" */ + case 0x4d: map_led (LED_CHARGING); break; /* "External Power Connected" */ + + default: goto ignore; + } + break; + + case HID_UP_DIGITIZER: + switch (usage->hid & 0xff) { + case 0x30: /* TipPressure */ + if (!test_bit(BTN_TOUCH, input->keybit)) { + device->quirks |= HID_QUIRK_NOTOUCH; + set_bit(EV_KEY, input->evbit); + set_bit(BTN_TOUCH, input->keybit); } + map_abs_clear(ABS_PRESSURE); break; - case HID_UP_CONSUMER: /* USB HUT v1.1, pages 56-62 */ - - switch (usage->hid & HID_USAGE) { - case 0x000: goto ignore; - case 0x034: map_key_clear(KEY_SLEEP); break; - case 0x036: map_key_clear(BTN_MISC); break; - - case 0x040: map_key_clear(KEY_MENU); break; - case 0x045: map_key_clear(KEY_RADIO); break; - - case 0x083: map_key_clear(KEY_LAST); break; - case 0x088: map_key_clear(KEY_PC); break; - case 0x089: map_key_clear(KEY_TV); break; - case 0x08a: map_key_clear(KEY_WWW); break; - case 0x08b: map_key_clear(KEY_DVD); break; - case 0x08c: map_key_clear(KEY_PHONE); break; - case 0x08d: map_key_clear(KEY_PROGRAM); break; - case 0x08e: map_key_clear(KEY_VIDEOPHONE); break; - case 0x08f: map_key_clear(KEY_GAMES); break; - case 0x090: map_key_clear(KEY_MEMO); break; - case 0x091: map_key_clear(KEY_CD); break; - case 0x092: map_key_clear(KEY_VCR); break; - case 0x093: map_key_clear(KEY_TUNER); break; - case 0x094: map_key_clear(KEY_EXIT); break; - case 0x095: map_key_clear(KEY_HELP); break; - case 0x096: map_key_clear(KEY_TAPE); break; - case 0x097: map_key_clear(KEY_TV2); break; - case 0x098: map_key_clear(KEY_SAT); break; - case 0x09a: map_key_clear(KEY_PVR); break; - - case 0x09c: map_key_clear(KEY_CHANNELUP); break; - case 0x09d: map_key_clear(KEY_CHANNELDOWN); break; - case 0x0a0: map_key_clear(KEY_VCR2); break; - - case 0x0b0: map_key_clear(KEY_PLAY); break; - case 0x0b1: map_key_clear(KEY_PAUSE); break; - case 0x0b2: map_key_clear(KEY_RECORD); break; - case 0x0b3: map_key_clear(KEY_FASTFORWARD); break; - case 0x0b4: map_key_clear(KEY_REWIND); break; - case 0x0b5: map_key_clear(KEY_NEXTSONG); break; - case 0x0b6: map_key_clear(KEY_PREVIOUSSONG); break; - case 0x0b7: map_key_clear(KEY_STOPCD); break; - case 0x0b8: map_key_clear(KEY_EJECTCD); break; - case 0x0bc: map_key_clear(KEY_MEDIA_REPEAT); break; - - case 0x0cd: map_key_clear(KEY_PLAYPAUSE); break; - case 0x0e0: map_abs_clear(ABS_VOLUME); break; - case 0x0e2: map_key_clear(KEY_MUTE); break; - case 0x0e5: map_key_clear(KEY_BASSBOOST); break; - case 0x0e9: map_key_clear(KEY_VOLUMEUP); break; - case 0x0ea: map_key_clear(KEY_VOLUMEDOWN); break; - - case 0x182: map_key_clear(KEY_BOOKMARKS); break; - case 0x183: map_key_clear(KEY_CONFIG); break; - case 0x184: map_key_clear(KEY_WORDPROCESSOR); break; - case 0x185: map_key_clear(KEY_EDITOR); break; - case 0x186: map_key_clear(KEY_SPREADSHEET); break; - case 0x187: map_key_clear(KEY_GRAPHICSEDITOR); break; - case 0x188: map_key_clear(KEY_PRESENTATION); break; - case 0x189: map_key_clear(KEY_DATABASE); break; - case 0x18a: map_key_clear(KEY_MAIL); break; - case 0x18b: map_key_clear(KEY_NEWS); break; - case 0x18c: map_key_clear(KEY_VOICEMAIL); break; - case 0x18d: map_key_clear(KEY_ADDRESSBOOK); break; - case 0x18e: map_key_clear(KEY_CALENDAR); break; - case 0x191: map_key_clear(KEY_FINANCE); break; - case 0x192: map_key_clear(KEY_CALC); break; - case 0x194: map_key_clear(KEY_FILE); break; - case 0x196: map_key_clear(KEY_WWW); break; - case 0x19c: map_key_clear(KEY_LOGOFF); break; - case 0x19e: map_key_clear(KEY_COFFEE); break; - case 0x1a6: map_key_clear(KEY_HELP); break; - case 0x1a7: map_key_clear(KEY_DOCUMENTS); break; - case 0x1ab: map_key_clear(KEY_SPELLCHECK); break; - case 0x1b6: map_key_clear(KEY_MEDIA); break; - case 0x1b7: map_key_clear(KEY_SOUND); break; - case 0x1bc: map_key_clear(KEY_MESSENGER); break; - case 0x1bd: map_key_clear(KEY_INFO); break; - case 0x201: map_key_clear(KEY_NEW); break; - case 0x202: map_key_clear(KEY_OPEN); break; - case 0x203: map_key_clear(KEY_CLOSE); break; - case 0x204: map_key_clear(KEY_EXIT); break; - case 0x207: map_key_clear(KEY_SAVE); break; - case 0x208: map_key_clear(KEY_PRINT); break; - case 0x209: map_key_clear(KEY_PROPS); break; - case 0x21a: map_key_clear(KEY_UNDO); break; - case 0x21b: map_key_clear(KEY_COPY); break; - case 0x21c: map_key_clear(KEY_CUT); break; - case 0x21d: map_key_clear(KEY_PASTE); break; - case 0x21f: map_key_clear(KEY_FIND); break; - case 0x221: map_key_clear(KEY_SEARCH); break; - case 0x222: map_key_clear(KEY_GOTO); break; - case 0x223: map_key_clear(KEY_HOMEPAGE); break; - case 0x224: map_key_clear(KEY_BACK); break; - case 0x225: map_key_clear(KEY_FORWARD); break; - case 0x226: map_key_clear(KEY_STOP); break; - case 0x227: map_key_clear(KEY_REFRESH); break; - case 0x22a: map_key_clear(KEY_BOOKMARKS); break; - case 0x22d: map_key_clear(KEY_ZOOMIN); break; - case 0x22e: map_key_clear(KEY_ZOOMOUT); break; - case 0x22f: map_key_clear(KEY_ZOOMRESET); break; - case 0x233: map_key_clear(KEY_SCROLLUP); break; - case 0x234: map_key_clear(KEY_SCROLLDOWN); break; - case 0x238: map_rel(REL_HWHEEL); break; - case 0x25f: map_key_clear(KEY_CANCEL); break; - case 0x279: map_key_clear(KEY_REDO); break; - - case 0x289: map_key_clear(KEY_REPLY); break; - case 0x28b: map_key_clear(KEY_FORWARDMAIL); break; - case 0x28c: map_key_clear(KEY_SEND); break; - - default: goto ignore; + case 0x32: /* InRange */ + switch (field->physical & 0xff) { + case 0x21: map_key(BTN_TOOL_MOUSE); break; + case 0x22: map_key(BTN_TOOL_FINGER); break; + default: map_key(BTN_TOOL_PEN); break; } break; - case HID_UP_HPVENDOR: /* Reported on a Dutch layout HP5308 */ - - set_bit(EV_REP, input->evbit); - switch (usage->hid & HID_USAGE) { - case 0x021: map_key_clear(KEY_PRINT); break; - case 0x070: map_key_clear(KEY_HP); break; - case 0x071: map_key_clear(KEY_CAMERA); break; - case 0x072: map_key_clear(KEY_SOUND); break; - case 0x073: map_key_clear(KEY_QUESTION); break; - case 0x080: map_key_clear(KEY_EMAIL); break; - case 0x081: map_key_clear(KEY_CHAT); break; - case 0x082: map_key_clear(KEY_SEARCH); break; - case 0x083: map_key_clear(KEY_CONNECT); break; - case 0x084: map_key_clear(KEY_FINANCE); break; - case 0x085: map_key_clear(KEY_SPORT); break; - case 0x086: map_key_clear(KEY_SHOP); break; - default: goto ignore; - } + case 0x3c: /* Invert */ + map_key_clear(BTN_TOOL_RUBBER); break; - case HID_UP_MSVENDOR: - - goto ignore; - - case HID_UP_CUSTOM: /* Reported on Logitech and Apple USB keyboards */ - - set_bit(EV_REP, input->evbit); - switch(usage->hid & HID_USAGE) { - case 0x003: - /* The fn key on Apple USB keyboards */ - map_key_clear(KEY_FN); - hidinput_apple_setup(input); - break; + case 0x33: /* Touch */ + case 0x42: /* TipSwitch */ + case 0x43: /* TipSwitch2 */ + device->quirks &= ~HID_QUIRK_NOTOUCH; + map_key_clear(BTN_TOUCH); + break; - default: goto ignore; - } + case 0x44: /* BarrelSwitch */ + map_key_clear(BTN_STYLUS); break; - case HID_UP_LOGIVENDOR: + default: goto unknown; + } + break; + + case HID_UP_CONSUMER: /* USB HUT v1.1, pages 56-62 */ + switch (usage->hid & HID_USAGE) { + case 0x000: goto ignore; + case 0x034: map_key_clear(KEY_SLEEP); break; + case 0x036: map_key_clear(BTN_MISC); break; + + case 0x040: map_key_clear(KEY_MENU); break; + case 0x045: map_key_clear(KEY_RADIO); break; + + case 0x083: map_key_clear(KEY_LAST); break; + case 0x088: map_key_clear(KEY_PC); break; + case 0x089: map_key_clear(KEY_TV); break; + case 0x08a: map_key_clear(KEY_WWW); break; + case 0x08b: map_key_clear(KEY_DVD); break; + case 0x08c: map_key_clear(KEY_PHONE); break; + case 0x08d: map_key_clear(KEY_PROGRAM); break; + case 0x08e: map_key_clear(KEY_VIDEOPHONE); break; + case 0x08f: map_key_clear(KEY_GAMES); break; + case 0x090: map_key_clear(KEY_MEMO); break; + case 0x091: map_key_clear(KEY_CD); break; + case 0x092: map_key_clear(KEY_VCR); break; + case 0x093: map_key_clear(KEY_TUNER); break; + case 0x094: map_key_clear(KEY_EXIT); break; + case 0x095: map_key_clear(KEY_HELP); break; + case 0x096: map_key_clear(KEY_TAPE); break; + case 0x097: map_key_clear(KEY_TV2); break; + case 0x098: map_key_clear(KEY_SAT); break; + case 0x09a: map_key_clear(KEY_PVR); break; + + case 0x09c: map_key_clear(KEY_CHANNELUP); break; + case 0x09d: map_key_clear(KEY_CHANNELDOWN); break; + case 0x0a0: map_key_clear(KEY_VCR2); break; + + case 0x0b0: map_key_clear(KEY_PLAY); break; + case 0x0b1: map_key_clear(KEY_PAUSE); break; + case 0x0b2: map_key_clear(KEY_RECORD); break; + case 0x0b3: map_key_clear(KEY_FASTFORWARD); break; + case 0x0b4: map_key_clear(KEY_REWIND); break; + case 0x0b5: map_key_clear(KEY_NEXTSONG); break; + case 0x0b6: map_key_clear(KEY_PREVIOUSSONG); break; + case 0x0b7: map_key_clear(KEY_STOPCD); break; + case 0x0b8: map_key_clear(KEY_EJECTCD); break; + case 0x0bc: map_key_clear(KEY_MEDIA_REPEAT); break; + + case 0x0cd: map_key_clear(KEY_PLAYPAUSE); break; + case 0x0e0: map_abs_clear(ABS_VOLUME); break; + case 0x0e2: map_key_clear(KEY_MUTE); break; + case 0x0e5: map_key_clear(KEY_BASSBOOST); break; + case 0x0e9: map_key_clear(KEY_VOLUMEUP); break; + case 0x0ea: map_key_clear(KEY_VOLUMEDOWN); break; + + case 0x182: map_key_clear(KEY_BOOKMARKS); break; + case 0x183: map_key_clear(KEY_CONFIG); break; + case 0x184: map_key_clear(KEY_WORDPROCESSOR); break; + case 0x185: map_key_clear(KEY_EDITOR); break; + case 0x186: map_key_clear(KEY_SPREADSHEET); break; + case 0x187: map_key_clear(KEY_GRAPHICSEDITOR); break; + case 0x188: map_key_clear(KEY_PRESENTATION); break; + case 0x189: map_key_clear(KEY_DATABASE); break; + case 0x18a: map_key_clear(KEY_MAIL); break; + case 0x18b: map_key_clear(KEY_NEWS); break; + case 0x18c: map_key_clear(KEY_VOICEMAIL); break; + case 0x18d: map_key_clear(KEY_ADDRESSBOOK); break; + case 0x18e: map_key_clear(KEY_CALENDAR); break; + case 0x191: map_key_clear(KEY_FINANCE); break; + case 0x192: map_key_clear(KEY_CALC); break; + case 0x194: map_key_clear(KEY_FILE); break; + case 0x196: map_key_clear(KEY_WWW); break; + case 0x19c: map_key_clear(KEY_LOGOFF); break; + case 0x19e: map_key_clear(KEY_COFFEE); break; + case 0x1a6: map_key_clear(KEY_HELP); break; + case 0x1a7: map_key_clear(KEY_DOCUMENTS); break; + case 0x1ab: map_key_clear(KEY_SPELLCHECK); break; + case 0x1b6: map_key_clear(KEY_MEDIA); break; + case 0x1b7: map_key_clear(KEY_SOUND); break; + case 0x1bc: map_key_clear(KEY_MESSENGER); break; + case 0x1bd: map_key_clear(KEY_INFO); break; + case 0x201: map_key_clear(KEY_NEW); break; + case 0x202: map_key_clear(KEY_OPEN); break; + case 0x203: map_key_clear(KEY_CLOSE); break; + case 0x204: map_key_clear(KEY_EXIT); break; + case 0x207: map_key_clear(KEY_SAVE); break; + case 0x208: map_key_clear(KEY_PRINT); break; + case 0x209: map_key_clear(KEY_PROPS); break; + case 0x21a: map_key_clear(KEY_UNDO); break; + case 0x21b: map_key_clear(KEY_COPY); break; + case 0x21c: map_key_clear(KEY_CUT); break; + case 0x21d: map_key_clear(KEY_PASTE); break; + case 0x21f: map_key_clear(KEY_FIND); break; + case 0x221: map_key_clear(KEY_SEARCH); break; + case 0x222: map_key_clear(KEY_GOTO); break; + case 0x223: map_key_clear(KEY_HOMEPAGE); break; + case 0x224: map_key_clear(KEY_BACK); break; + case 0x225: map_key_clear(KEY_FORWARD); break; + case 0x226: map_key_clear(KEY_STOP); break; + case 0x227: map_key_clear(KEY_REFRESH); break; + case 0x22a: map_key_clear(KEY_BOOKMARKS); break; + case 0x22d: map_key_clear(KEY_ZOOMIN); break; + case 0x22e: map_key_clear(KEY_ZOOMOUT); break; + case 0x22f: map_key_clear(KEY_ZOOMRESET); break; + case 0x233: map_key_clear(KEY_SCROLLUP); break; + case 0x234: map_key_clear(KEY_SCROLLDOWN); break; + case 0x238: map_rel(REL_HWHEEL); break; + case 0x25f: map_key_clear(KEY_CANCEL); break; + case 0x279: map_key_clear(KEY_REDO); break; + + case 0x289: map_key_clear(KEY_REPLY); break; + case 0x28b: map_key_clear(KEY_FORWARDMAIL); break; + case 0x28c: map_key_clear(KEY_SEND); break; + + default: goto ignore; + } + break; + + case HID_UP_HPVENDOR: /* Reported on a Dutch layout HP5308 */ + set_bit(EV_REP, input->evbit); + switch (usage->hid & HID_USAGE) { + case 0x021: map_key_clear(KEY_PRINT); break; + case 0x070: map_key_clear(KEY_HP); break; + case 0x071: map_key_clear(KEY_CAMERA); break; + case 0x072: map_key_clear(KEY_SOUND); break; + case 0x073: map_key_clear(KEY_QUESTION); break; + case 0x080: map_key_clear(KEY_EMAIL); break; + case 0x081: map_key_clear(KEY_CHAT); break; + case 0x082: map_key_clear(KEY_SEARCH); break; + case 0x083: map_key_clear(KEY_CONNECT); break; + case 0x084: map_key_clear(KEY_FINANCE); break; + case 0x085: map_key_clear(KEY_SPORT); break; + case 0x086: map_key_clear(KEY_SHOP); break; + default: goto ignore; + } + break; - goto ignore; - - case HID_UP_PID: + case HID_UP_MSVENDOR: + goto ignore; - switch(usage->hid & HID_USAGE) { - case 0xa4: map_key_clear(BTN_DEAD); break; - default: goto ignore; - } - break; + case HID_UP_CUSTOM: /* Reported on Logitech and Apple USB keyboards */ + set_bit(EV_REP, input->evbit); + goto ignore; - default: - unknown: - if (field->report_size == 1) { - if (field->report->type == HID_OUTPUT_REPORT) { - map_led(LED_MISC); - break; - } - map_key(BTN_MISC); - break; - } - if (field->flags & HID_MAIN_ITEM_RELATIVE) { - map_rel(REL_MISC); + case HID_UP_LOGIVENDOR: + goto ignore; + + case HID_UP_PID: + switch (usage->hid & HID_USAGE) { + case 0xa4: map_key_clear(BTN_DEAD); break; + default: goto ignore; + } + break; + + default: + unknown: + if (field->report_size == 1) { + if (field->report->type == HID_OUTPUT_REPORT) { + map_led(LED_MISC); break; } - map_abs(ABS_MISC); + map_key(BTN_MISC); + break; + } + if (field->flags & HID_MAIN_ITEM_RELATIVE) { + map_rel(REL_MISC); break; + } + map_abs(ABS_MISC); + break; } mapped: - if (device->quirks & HID_QUIRK_MIGHTYMOUSE) { - if (usage->hid == HID_GD_Z) - map_rel(REL_HWHEEL); - else if (usage->code == BTN_1) - map_key(BTN_2); - else if (usage->code == BTN_2) - map_key(BTN_1); - } - - if ((device->quirks & (HID_QUIRK_2WHEEL_MOUSE_HACK_7 | HID_QUIRK_2WHEEL_MOUSE_HACK_5 | - HID_QUIRK_2WHEEL_MOUSE_HACK_B8)) && (usage->type == EV_REL) && - (usage->code == REL_WHEEL)) - set_bit(REL_HWHEEL, bit); - - if (((device->quirks & HID_QUIRK_2WHEEL_MOUSE_HACK_5) && (usage->hid == 0x00090005)) - || ((device->quirks & HID_QUIRK_2WHEEL_MOUSE_HACK_7) && (usage->hid == 0x00090007))) + if (device->driver->input_mapped && device->driver->input_mapped(device, + hidinput, field, usage, &bit, &max) < 0) goto ignore; - if ((device->quirks & HID_QUIRK_BAD_RELATIVE_KEYS) && - usage->type == EV_KEY && (field->flags & HID_MAIN_ITEM_RELATIVE)) - field->flags &= ~HID_MAIN_ITEM_RELATIVE; - set_bit(usage->type, input->evbit); - if (device->quirks & HID_QUIRK_DUPLICATE_USAGES && - (usage->type == EV_KEY || - usage->type == EV_REL || - usage->type == EV_ABS)) - clear_bit(usage->code, bit); - while (usage->code <= max && test_and_set_bit(usage->code, bit)) usage->code = find_next_zero_bit(bit, max + 1, usage->code); @@ -858,10 +585,6 @@ void hidinput_hid_event(struct hid_device *hid, struct hid_field *field, struct if (!usage->type) return; - /* handle input events for quirky devices */ - if (hidinput_event_quirks(hid, field, usage, value)) - return; - if (usage->hat_min < usage->hat_max || usage->hat_dir) { int hat_dir = usage->hat_dir; if (!hat_dir) @@ -961,14 +684,14 @@ static int hidinput_open(struct input_dev *dev) { struct hid_device *hid = input_get_drvdata(dev); - return hid->hid_open(hid); + return hid->ll_driver->open(hid); } static void hidinput_close(struct input_dev *dev) { struct hid_device *hid = input_get_drvdata(dev); - hid->hid_close(hid); + hid->ll_driver->close(hid); } /* @@ -977,7 +700,7 @@ static void hidinput_close(struct input_dev *dev) * Read all reports and initialize the absolute field values. */ -int hidinput_connect(struct hid_device *hid) +int hidinput_connect(struct hid_device *hid, unsigned int force) { struct hid_report *report; struct hid_input *hidinput = NULL; @@ -985,19 +708,20 @@ int hidinput_connect(struct hid_device *hid) int i, j, k; int max_report_type = HID_OUTPUT_REPORT; - if (hid->quirks & HID_QUIRK_IGNORE_HIDINPUT) - return -1; - INIT_LIST_HEAD(&hid->inputs); - for (i = 0; i < hid->maxcollection; i++) - if (hid->collection[i].type == HID_COLLECTION_APPLICATION || - hid->collection[i].type == HID_COLLECTION_PHYSICAL) - if (IS_INPUT_APPLICATION(hid->collection[i].usage)) - break; + if (!force) { + for (i = 0; i < hid->maxcollection; i++) { + struct hid_collection *col = &hid->collection[i]; + if (col->type == HID_COLLECTION_APPLICATION || + col->type == HID_COLLECTION_PHYSICAL) + if (IS_INPUT_APPLICATION(col->usage)) + break; + } - if (i == hid->maxcollection && (hid->quirks & HID_QUIRK_HIDINPUT) == 0) - return -1; + if (i == hid->maxcollection) + return -1; + } if (hid->quirks & HID_QUIRK_SKIP_OUTPUT_REPORTS) max_report_type = HID_INPUT_REPORT; @@ -1019,7 +743,8 @@ int hidinput_connect(struct hid_device *hid) } input_set_drvdata(input_dev, hid); - input_dev->event = hid->hidinput_input_event; + input_dev->event = + hid->ll_driver->hidinput_input_event; input_dev->open = hidinput_open; input_dev->close = hidinput_close; input_dev->setkeycode = hidinput_setkeycode; @@ -1032,7 +757,7 @@ int hidinput_connect(struct hid_device *hid) input_dev->id.vendor = hid->vendor; input_dev->id.product = hid->product; input_dev->id.version = hid->version; - input_dev->dev.parent = hid->dev; + input_dev->dev.parent = hid->dev.parent; hidinput->input = input_dev; list_add_tail(&hidinput->list, &hid->inputs); } diff --git a/drivers/hid/hid-lg.c b/drivers/hid/hid-lg.c new file mode 100644 index 000000000000..406d8c82abf1 --- /dev/null +++ b/drivers/hid/hid-lg.c @@ -0,0 +1,342 @@ +/* + * HID driver for some logitech "special" devices + * + * 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) 2006-2007 Jiri Kosina + * Copyright (c) 2007 Paul Walmsley + * Copyright (c) 2008 Jiri Slaby + */ + +/* + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the Free + * Software Foundation; either version 2 of the License, or (at your option) + * any later version. + */ + +#include <linux/device.h> +#include <linux/hid.h> +#include <linux/module.h> + +#include "hid-ids.h" +#include "hid-lg.h" + +#define LG_RDESC 0x001 +#define LG_BAD_RELATIVE_KEYS 0x002 +#define LG_DUPLICATE_USAGES 0x004 +#define LG_RESET_LEDS 0x008 +#define LG_EXPANDED_KEYMAP 0x010 +#define LG_IGNORE_DOUBLED_WHEEL 0x020 +#define LG_WIRELESS 0x040 +#define LG_INVERT_HWHEEL 0x080 +#define LG_NOGET 0x100 +#define LG_FF 0x200 +#define LG_FF2 0x400 + +/* + * Certain Logitech keyboards send in report #3 keys which are far + * above the logical maximum described in descriptor. This extends + * the original value of 0x28c of logical maximum to 0x104d + */ +static void lg_report_fixup(struct hid_device *hdev, __u8 *rdesc, + unsigned int rsize) +{ + unsigned long quirks = (unsigned long)hid_get_drvdata(hdev); + + if ((quirks & LG_RDESC) && rsize >= 90 && rdesc[83] == 0x26 && + rdesc[84] == 0x8c && rdesc[85] == 0x02) { + dev_info(&hdev->dev, "fixing up Logitech keyboard report " + "descriptor\n"); + rdesc[84] = rdesc[89] = 0x4d; + rdesc[85] = rdesc[90] = 0x10; + } +} + +#define lg_map_key_clear(c) hid_map_usage_clear(hi, usage, bit, max, \ + EV_KEY, (c)) + +static int lg_ultrax_remote_mapping(struct hid_input *hi, + struct hid_usage *usage, unsigned long **bit, int *max) +{ + if ((usage->hid & HID_USAGE_PAGE) != HID_UP_LOGIVENDOR) + return 0; + + set_bit(EV_REP, hi->input->evbit); + switch (usage->hid & HID_USAGE) { + /* Reported on Logitech Ultra X Media Remote */ + case 0x004: lg_map_key_clear(KEY_AGAIN); break; + case 0x00d: lg_map_key_clear(KEY_HOME); break; + case 0x024: lg_map_key_clear(KEY_SHUFFLE); break; + case 0x025: lg_map_key_clear(KEY_TV); break; + case 0x026: lg_map_key_clear(KEY_MENU); break; + case 0x031: lg_map_key_clear(KEY_AUDIO); break; + case 0x032: lg_map_key_clear(KEY_TEXT); break; + case 0x033: lg_map_key_clear(KEY_LAST); break; + case 0x047: lg_map_key_clear(KEY_MP3); break; + case 0x048: lg_map_key_clear(KEY_DVD); break; + case 0x049: lg_map_key_clear(KEY_MEDIA); break; + case 0x04a: lg_map_key_clear(KEY_VIDEO); break; + case 0x04b: lg_map_key_clear(KEY_ANGLE); break; + case 0x04c: lg_map_key_clear(KEY_LANGUAGE); break; + case 0x04d: lg_map_key_clear(KEY_SUBTITLE); break; + case 0x051: lg_map_key_clear(KEY_RED); break; + case 0x052: lg_map_key_clear(KEY_CLOSE); break; + + default: + return 0; + } + return 1; +} + +static int lg_wireless_mapping(struct hid_input *hi, struct hid_usage *usage, + unsigned long **bit, int *max) +{ + if ((usage->hid & HID_USAGE_PAGE) != HID_UP_CONSUMER) + return 0; + + switch (usage->hid & HID_USAGE) { + case 0x1001: lg_map_key_clear(KEY_MESSENGER); break; + case 0x1003: lg_map_key_clear(KEY_SOUND); break; + case 0x1004: lg_map_key_clear(KEY_VIDEO); break; + case 0x1005: lg_map_key_clear(KEY_AUDIO); break; + case 0x100a: lg_map_key_clear(KEY_DOCUMENTS); break; + case 0x1011: lg_map_key_clear(KEY_PREVIOUSSONG); break; + case 0x1012: lg_map_key_clear(KEY_NEXTSONG); break; + case 0x1013: lg_map_key_clear(KEY_CAMERA); break; + case 0x1014: lg_map_key_clear(KEY_MESSENGER); break; + case 0x1015: lg_map_key_clear(KEY_RECORD); break; + case 0x1016: lg_map_key_clear(KEY_PLAYER); break; + case 0x1017: lg_map_key_clear(KEY_EJECTCD); break; + case 0x1018: lg_map_key_clear(KEY_MEDIA); break; + case 0x1019: lg_map_key_clear(KEY_PROG1); break; + case 0x101a: lg_map_key_clear(KEY_PROG2); break; + case 0x101b: lg_map_key_clear(KEY_PROG3); break; + case 0x101f: lg_map_key_clear(KEY_ZOOMIN); break; + case 0x1020: lg_map_key_clear(KEY_ZOOMOUT); break; + case 0x1021: lg_map_key_clear(KEY_ZOOMRESET); break; + case 0x1023: lg_map_key_clear(KEY_CLOSE); break; + case 0x1027: lg_map_key_clear(KEY_MENU); break; + /* this one is marked as 'Rotate' */ + case 0x1028: lg_map_key_clear(KEY_ANGLE); break; + case 0x1029: lg_map_key_clear(KEY_SHUFFLE); break; + case 0x102a: lg_map_key_clear(KEY_BACK); break; + case 0x102b: lg_map_key_clear(KEY_CYCLEWINDOWS); break; + case 0x1041: lg_map_key_clear(KEY_BATTERY); break; + case 0x1042: lg_map_key_clear(KEY_WORDPROCESSOR); break; + case 0x1043: lg_map_key_clear(KEY_SPREADSHEET); break; + case 0x1044: lg_map_key_clear(KEY_PRESENTATION); break; + case 0x1045: lg_map_key_clear(KEY_UNDO); break; + case 0x1046: lg_map_key_clear(KEY_REDO); break; + case 0x1047: lg_map_key_clear(KEY_PRINT); break; + case 0x1048: lg_map_key_clear(KEY_SAVE); break; + case 0x1049: lg_map_key_clear(KEY_PROG1); break; + case 0x104a: lg_map_key_clear(KEY_PROG2); break; + case 0x104b: lg_map_key_clear(KEY_PROG3); break; + case 0x104c: lg_map_key_clear(KEY_PROG4); break; + + default: + return 0; + } + return 1; +} + +static int lg_input_mapping(struct hid_device *hdev, struct hid_input *hi, + struct hid_field *field, struct hid_usage *usage, + unsigned long **bit, int *max) +{ + /* extended mapping for certain Logitech hardware (Logitech cordless + desktop LX500) */ + static const u8 e_keymap[] = { + 0,216, 0,213,175,156, 0, 0, 0, 0, + 144, 0, 0, 0, 0, 0, 0, 0, 0,212, + 174,167,152,161,112, 0, 0, 0,154, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0,183,184,185,186,187, + 188,189,190,191,192,193,194, 0, 0, 0 + }; + unsigned long quirks = (unsigned long)hid_get_drvdata(hdev); + unsigned int hid = usage->hid; + + if (hdev->product == USB_DEVICE_ID_LOGITECH_RECEIVER && + lg_ultrax_remote_mapping(hi, usage, bit, max)) + return 1; + + if ((quirks & LG_WIRELESS) && lg_wireless_mapping(hi, usage, bit, max)) + return 1; + + if ((hid & HID_USAGE_PAGE) != HID_UP_BUTTON) + return 0; + + hid &= HID_USAGE; + + /* Special handling for Logitech Cordless Desktop */ + if (field->application == HID_GD_MOUSE) { + if ((quirks & LG_IGNORE_DOUBLED_WHEEL) && + (hid == 7 || hid == 8)) + return -1; + } else { + if ((quirks & LG_EXPANDED_KEYMAP) && + hid < ARRAY_SIZE(e_keymap) && + e_keymap[hid] != 0) { + hid_map_usage(hi, usage, bit, max, EV_KEY, + e_keymap[hid]); + return 1; + } + } + + return 0; +} + +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) +{ + unsigned long quirks = (unsigned long)hid_get_drvdata(hdev); + + if ((quirks & LG_BAD_RELATIVE_KEYS) && usage->type == EV_KEY && + (field->flags & HID_MAIN_ITEM_RELATIVE)) + field->flags &= ~HID_MAIN_ITEM_RELATIVE; + + if ((quirks & LG_DUPLICATE_USAGES) && (usage->type == EV_KEY || + usage->type == EV_REL || usage->type == EV_ABS)) + clear_bit(usage->code, *bit); + + return 0; +} + +static int lg_event(struct hid_device *hdev, struct hid_field *field, + struct hid_usage *usage, __s32 value) +{ + unsigned long quirks = (unsigned long)hid_get_drvdata(hdev); + + if ((quirks & LG_INVERT_HWHEEL) && usage->code == REL_HWHEEL) { + input_event(field->hidinput->input, usage->type, usage->code, + -value); + return 1; + } + + return 0; +} + +static int lg_probe(struct hid_device *hdev, const struct hid_device_id *id) +{ + unsigned long quirks = id->driver_data; + unsigned int connect_mask = HID_CONNECT_DEFAULT; + int ret; + + hid_set_drvdata(hdev, (void *)quirks); + + if (quirks & LG_NOGET) + hdev->quirks |= HID_QUIRK_NOGET; + + ret = hid_parse(hdev); + if (ret) { + dev_err(&hdev->dev, "parse failed\n"); + goto err_free; + } + + if (quirks & (LG_FF | LG_FF2)) + connect_mask &= ~HID_CONNECT_FF; + + ret = hid_hw_start(hdev, connect_mask); + if (ret) { + dev_err(&hdev->dev, "hw start failed\n"); + goto err_free; + } + + if (quirks & LG_RESET_LEDS) + usbhid_set_leds(hdev); + + if (quirks & LG_FF) + lgff_init(hdev); + if (quirks & LG_FF2) + lg2ff_init(hdev); + + return 0; +err_free: + return ret; +} + +static const struct hid_device_id lg_devices[] = { + { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_MX3000_RECEIVER), + .driver_data = LG_RDESC | LG_WIRELESS }, + { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_S510_RECEIVER), + .driver_data = LG_RDESC | LG_WIRELESS }, + { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_S510_RECEIVER_2), + .driver_data = LG_RDESC | LG_WIRELESS }, + + { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_RECEIVER), + .driver_data = LG_BAD_RELATIVE_KEYS }, + + { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_DINOVO_DESKTOP), + .driver_data = LG_DUPLICATE_USAGES }, + { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_DINOVO_EDGE), + .driver_data = LG_DUPLICATE_USAGES }, + { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_DINOVO_MINI), + .driver_data = LG_DUPLICATE_USAGES }, + + { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_KBD), + .driver_data = LG_RESET_LEDS }, + + { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_ELITE_KBD), + .driver_data = LG_IGNORE_DOUBLED_WHEEL | LG_EXPANDED_KEYMAP }, + { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_CORDLESS_DESKTOP_LX500), + .driver_data = LG_IGNORE_DOUBLED_WHEEL | LG_EXPANDED_KEYMAP }, + + { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_LX3), + .driver_data = LG_INVERT_HWHEEL }, + { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_V150), + .driver_data = LG_INVERT_HWHEEL }, + + { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_EXTREME_3D), + .driver_data = LG_NOGET }, + { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_WHEEL), + .driver_data = LG_NOGET | LG_FF }, + + { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_RUMBLEPAD), + .driver_data = LG_FF }, + { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_RUMBLEPAD2_2), + .driver_data = LG_FF }, + { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_WINGMAN_F3D), + .driver_data = LG_FF }, + { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_FORCE3D_PRO), + .driver_data = LG_FF }, + { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_MOMO_WHEEL), + .driver_data = LG_FF }, + { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_MOMO_WHEEL2), + .driver_data = LG_FF }, + { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_RUMBLEPAD2), + .driver_data = LG_FF2 }, + { } +}; +MODULE_DEVICE_TABLE(hid, lg_devices); + +static struct hid_driver lg_driver = { + .name = "logitech", + .id_table = lg_devices, + .report_fixup = lg_report_fixup, + .input_mapping = lg_input_mapping, + .input_mapped = lg_input_mapped, + .event = lg_event, + .probe = lg_probe, +}; + +static int lg_init(void) +{ + return hid_register_driver(&lg_driver); +} + +static void lg_exit(void) +{ + hid_unregister_driver(&lg_driver); +} + +module_init(lg_init); +module_exit(lg_exit); +MODULE_LICENSE("GPL"); + +HID_COMPAT_LOAD_DRIVER(logitech); diff --git a/drivers/hid/hid-lg.h b/drivers/hid/hid-lg.h new file mode 100644 index 000000000000..27ae750ca878 --- /dev/null +++ b/drivers/hid/hid-lg.h @@ -0,0 +1,18 @@ +#ifndef __HID_LG_H +#define __HID_LG_H + +#include <linux/autoconf.h> + +#ifdef CONFIG_LOGITECH_FF +int lgff_init(struct hid_device *hdev); +#else +static inline int lgff_init(struct hid_device *hdev) { return -1; } +#endif + +#ifdef CONFIG_LOGIRUMBLEPAD2_FF +int lg2ff_init(struct hid_device *hdev); +#else +static inline int lg2ff_init(struct hid_device *hdev) { return -1; } +#endif + +#endif diff --git a/drivers/hid/usbhid/hid-lg2ff.c b/drivers/hid/hid-lg2ff.c index d469bd0061c9..4e6dc6e26523 100644 --- a/drivers/hid/usbhid/hid-lg2ff.c +++ b/drivers/hid/hid-lg2ff.c @@ -24,7 +24,9 @@ #include <linux/input.h> #include <linux/usb.h> #include <linux/hid.h> -#include "usbhid.h" + +#include "usbhid/usbhid.h" +#include "hid-lg.h" struct lg2ff_device { struct hid_report *report; @@ -57,7 +59,7 @@ static int play_effect(struct input_dev *dev, void *data, return 0; } -int hid_lg2ff_init(struct hid_device *hid) +int lg2ff_init(struct hid_device *hid) { struct lg2ff_device *lg2ff; struct hid_report *report; @@ -69,18 +71,18 @@ int hid_lg2ff_init(struct hid_device *hid) int error; if (list_empty(report_list)) { - printk(KERN_ERR "hid-lg2ff: no output report found\n"); + dev_err(&hid->dev, "no output report found\n"); return -ENODEV; } report = list_entry(report_list->next, struct hid_report, list); if (report->maxfield < 1) { - printk(KERN_ERR "hid-lg2ff: output report is empty\n"); + dev_err(&hid->dev, "output report is empty\n"); return -ENODEV; } if (report->field[0]->report_count < 7) { - printk(KERN_ERR "hid-lg2ff: not enough values in the field\n"); + dev_err(&hid->dev, "not enough values in the field\n"); return -ENODEV; } @@ -107,7 +109,7 @@ int hid_lg2ff_init(struct hid_device *hid) usbhid_submit_report(hid, report, USB_DIR_OUT); - printk(KERN_INFO "Force feedback for Logitech Rumblepad 2 by " + dev_info(&hid->dev, "Force feedback for Logitech Rumblepad 2 by " "Anssi Hannula <anssi.hannula@gmail.com>\n"); return 0; diff --git a/drivers/hid/usbhid/hid-lgff.c b/drivers/hid/hid-lgff.c index 4b7ab6a46d93..51aff08e10ce 100644 --- a/drivers/hid/usbhid/hid-lgff.c +++ b/drivers/hid/hid-lgff.c @@ -30,7 +30,9 @@ #include <linux/input.h> #include <linux/usb.h> #include <linux/hid.h> -#include "usbhid.h" + +#include "usbhid/usbhid.h" +#include "hid-lg.h" struct dev_type { u16 idVendor; @@ -48,6 +50,12 @@ static const signed short ff_joystick[] = { -1 }; +static const signed short ff_wheel[] = { + FF_CONSTANT, + FF_AUTOCENTER, + -1 +}; + static const struct dev_type devices[] = { { 0x046d, 0xc211, ff_rumble }, { 0x046d, 0xc219, ff_rumble }, @@ -55,7 +63,7 @@ static const struct dev_type devices[] = { { 0x046d, 0xc286, ff_joystick }, { 0x046d, 0xc294, ff_joystick }, { 0x046d, 0xc295, ff_joystick }, - { 0x046d, 0xca03, ff_joystick }, + { 0x046d, 0xca03, ff_wheel }, }; static int hid_lgff_play(struct input_dev *dev, void *data, struct ff_effect *effect) @@ -100,7 +108,24 @@ static int hid_lgff_play(struct input_dev *dev, void *data, struct ff_effect *ef return 0; } -int hid_lgff_init(struct hid_device* hid) +static void hid_lgff_set_autocenter(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 >> 12) & 0xf; + *value++ = 0xfe; + *value++ = 0x0d; + *value++ = magnitude; /* clockwise strength */ + *value++ = magnitude; /* counter-clockwise strength */ + *value++ = 0x80; + *value++ = 0x00; + *value = 0x00; + usbhid_submit_report(hid, report, USB_DIR_OUT); +} + +int lgff_init(struct hid_device* hid) { struct hid_input *hidinput = list_entry(hid->inputs.next, struct hid_input, list); struct list_head *report_list = &hid->report_enum[HID_OUTPUT_REPORT].report_list; @@ -145,6 +170,9 @@ int hid_lgff_init(struct hid_device* hid) if (error) return error; + if ( test_bit(FF_AUTOCENTER, dev->ffbit) ) + dev->ff->set_autocenter = hid_lgff_set_autocenter; + printk(KERN_INFO "Force feedback for Logitech force feedback devices by Johann Deneux <johann.deneux@it.uu.se>\n"); return 0; diff --git a/drivers/hid/hid-microsoft.c b/drivers/hid/hid-microsoft.c new file mode 100644 index 000000000000..d718b1607d0f --- /dev/null +++ b/drivers/hid/hid-microsoft.c @@ -0,0 +1,219 @@ +/* + * HID driver for some microsoft "special" devices + * + * 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) 2006-2007 Jiri Kosina + * Copyright (c) 2007 Paul Walmsley + * Copyright (c) 2008 Jiri Slaby + */ + +/* + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the Free + * Software Foundation; either version 2 of the License, or (at your option) + * any later version. + */ + +#include <linux/device.h> +#include <linux/input.h> +#include <linux/hid.h> +#include <linux/module.h> + +#include "hid-ids.h" + +#define MS_HIDINPUT 0x01 +#define MS_ERGONOMY 0x02 +#define MS_PRESENTER 0x04 +#define MS_RDESC 0x08 +#define MS_NOGET 0x10 + +/* + * Microsoft Wireless Desktop Receiver (Model 1028) has several + * 'Usage Min/Max' where it ought to have 'Physical Min/Max' + */ +static void ms_report_fixup(struct hid_device *hdev, __u8 *rdesc, + unsigned int rsize) +{ + unsigned long quirks = (unsigned long)hid_get_drvdata(hdev); + + if ((quirks & MS_RDESC) && rsize == 571 && rdesc[284] == 0x19 && + rdesc[286] == 0x2a && rdesc[304] == 0x19 && + rdesc[306] == 0x29 && rdesc[352] == 0x1a && + rdesc[355] == 0x2a && rdesc[557] == 0x19 && + rdesc[559] == 0x29) { + dev_info(&hdev->dev, "fixing up Microsoft Wireless Receiver " + "Model 1028 report descriptor\n"); + rdesc[284] = rdesc[304] = rdesc[557] = 0x35; + rdesc[352] = 0x36; + rdesc[286] = rdesc[355] = 0x46; + rdesc[306] = rdesc[559] = 0x45; + } +} + +#define ms_map_key_clear(c) hid_map_usage_clear(hi, usage, bit, max, \ + EV_KEY, (c)) +static int ms_ergonomy_kb_quirk(struct hid_input *hi, struct hid_usage *usage, + unsigned long **bit, int *max) +{ + struct input_dev *input = hi->input; + + switch (usage->hid & HID_USAGE) { + case 0xfd06: ms_map_key_clear(KEY_CHAT); break; + case 0xfd07: ms_map_key_clear(KEY_PHONE); break; + case 0xff05: + set_bit(EV_REP, input->evbit); + ms_map_key_clear(KEY_F13); + set_bit(KEY_F14, input->keybit); + set_bit(KEY_F15, input->keybit); + set_bit(KEY_F16, input->keybit); + set_bit(KEY_F17, input->keybit); + set_bit(KEY_F18, input->keybit); + default: + return 0; + } + return 1; +} + +static int ms_presenter_8k_quirk(struct hid_input *hi, struct hid_usage *usage, + unsigned long **bit, int *max) +{ + set_bit(EV_REP, hi->input->evbit); + switch (usage->hid & HID_USAGE) { + case 0xfd08: ms_map_key_clear(KEY_FORWARD); break; + case 0xfd09: ms_map_key_clear(KEY_BACK); break; + case 0xfd0b: ms_map_key_clear(KEY_PLAYPAUSE); break; + case 0xfd0e: ms_map_key_clear(KEY_CLOSE); break; + case 0xfd0f: ms_map_key_clear(KEY_PLAY); break; + default: + return 0; + } + return 1; +} + +static int ms_input_mapping(struct hid_device *hdev, struct hid_input *hi, + struct hid_field *field, struct hid_usage *usage, + unsigned long **bit, int *max) +{ + unsigned long quirks = (unsigned long)hid_get_drvdata(hdev); + + if ((usage->hid & HID_USAGE_PAGE) != HID_UP_MSVENDOR) + return 0; + + if (quirks & MS_ERGONOMY) { + int ret = ms_ergonomy_kb_quirk(hi, usage, bit, max); + if (ret) + return ret; + } + + if ((quirks & MS_PRESENTER) && + ms_presenter_8k_quirk(hi, usage, bit, max)) + return 1; + + return 0; +} + +static int ms_event(struct hid_device *hdev, struct hid_field *field, + struct hid_usage *usage, __s32 value) +{ + unsigned long quirks = (unsigned long)hid_get_drvdata(hdev); + + if (!(hdev->claimed & HID_CLAIMED_INPUT) || !field->hidinput || + !usage->type) + return 0; + + /* Handling MS keyboards special buttons */ + if (quirks & MS_ERGONOMY && usage->hid == (HID_UP_MSVENDOR | 0xff05)) { + struct input_dev *input = field->hidinput->input; + static unsigned int last_key = 0; + unsigned int key = 0; + switch (value) { + case 0x01: key = KEY_F14; break; + case 0x02: key = KEY_F15; break; + case 0x04: key = KEY_F16; break; + case 0x08: key = KEY_F17; break; + case 0x10: key = KEY_F18; break; + } + if (key) { + input_event(input, usage->type, key, 1); + last_key = key; + } else + input_event(input, usage->type, last_key, 0); + + return 1; + } + + return 0; +} + +static int ms_probe(struct hid_device *hdev, const struct hid_device_id *id) +{ + unsigned long quirks = id->driver_data; + int ret; + + hid_set_drvdata(hdev, (void *)quirks); + + if (quirks & MS_NOGET) + hdev->quirks |= HID_QUIRK_NOGET; + + ret = hid_parse(hdev); + if (ret) { + dev_err(&hdev->dev, "parse failed\n"); + goto err_free; + } + + ret = hid_hw_start(hdev, HID_CONNECT_DEFAULT | ((quirks & MS_HIDINPUT) ? + HID_CONNECT_HIDINPUT_FORCE : 0)); + if (ret) { + dev_err(&hdev->dev, "hw start failed\n"); + goto err_free; + } + + return 0; +err_free: + return ret; +} + +static const struct hid_device_id ms_devices[] = { + { HID_USB_DEVICE(USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_SIDEWINDER_GV), + .driver_data = MS_HIDINPUT }, + { HID_USB_DEVICE(USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_MS_NE4K), + .driver_data = MS_ERGONOMY }, + { HID_USB_DEVICE(USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_MS_LK6K), + .driver_data = MS_ERGONOMY | MS_RDESC }, + { HID_USB_DEVICE(USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_MS_PRESENTER_8K_USB), + .driver_data = MS_PRESENTER }, + { HID_USB_DEVICE(USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_WIRELESS_OPTICAL_DESKTOP_3_0), + .driver_data = MS_NOGET }, + + { HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_MS_PRESENTER_8K_BT), + .driver_data = MS_PRESENTER }, + { } +}; +MODULE_DEVICE_TABLE(hid, ms_devices); + +static struct hid_driver ms_driver = { + .name = "microsoft", + .id_table = ms_devices, + .report_fixup = ms_report_fixup, + .input_mapping = ms_input_mapping, + .event = ms_event, + .probe = ms_probe, +}; + +static int ms_init(void) +{ + return hid_register_driver(&ms_driver); +} + +static void ms_exit(void) +{ + hid_unregister_driver(&ms_driver); +} + +module_init(ms_init); +module_exit(ms_exit); +MODULE_LICENSE("GPL"); + +HID_COMPAT_LOAD_DRIVER(microsoft); diff --git a/drivers/hid/hid-monterey.c b/drivers/hid/hid-monterey.c new file mode 100644 index 000000000000..f3a85a065f18 --- /dev/null +++ b/drivers/hid/hid-monterey.c @@ -0,0 +1,82 @@ +/* + * HID driver for some monterey "special" devices + * + * 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) 2006-2007 Jiri Kosina + * Copyright (c) 2007 Paul Walmsley + * Copyright (c) 2008 Jiri Slaby + */ + +/* + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the Free + * Software Foundation; either version 2 of the License, or (at your option) + * any later version. + */ + +#include <linux/device.h> +#include <linux/hid.h> +#include <linux/module.h> + +#include "hid-ids.h" + +static void mr_report_fixup(struct hid_device *hdev, __u8 *rdesc, + unsigned int rsize) +{ + if (rsize >= 30 && rdesc[29] == 0x05 && rdesc[30] == 0x09) { + dev_info(&hdev->dev, "fixing up button/consumer in HID report " + "descriptor\n"); + rdesc[30] = 0x0c; + } +} + +#define mr_map_key_clear(c) hid_map_usage_clear(hi, usage, bit, max, \ + EV_KEY, (c)) +static int mr_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) != HID_UP_CONSUMER) + return 0; + + switch (usage->hid & HID_USAGE) { + case 0x156: mr_map_key_clear(KEY_WORDPROCESSOR); break; + case 0x157: mr_map_key_clear(KEY_SPREADSHEET); break; + case 0x158: mr_map_key_clear(KEY_PRESENTATION); break; + case 0x15c: mr_map_key_clear(KEY_STOP); break; + default: + return 0; + } + return 1; +} + +static const struct hid_device_id mr_devices[] = { + { HID_USB_DEVICE(USB_VENDOR_ID_MONTEREY, USB_DEVICE_ID_GENIUS_KB29E) }, + { } +}; +MODULE_DEVICE_TABLE(hid, mr_devices); + +static struct hid_driver mr_driver = { + .name = "monterey", + .id_table = mr_devices, + .report_fixup = mr_report_fixup, + .input_mapping = mr_input_mapping, +}; + +static int mr_init(void) +{ + return hid_register_driver(&mr_driver); +} + +static void mr_exit(void) +{ + hid_unregister_driver(&mr_driver); +} + +module_init(mr_init); +module_exit(mr_exit); +MODULE_LICENSE("GPL"); + +HID_COMPAT_LOAD_DRIVER(monterey); diff --git a/drivers/hid/hid-petalynx.c b/drivers/hid/hid-petalynx.c new file mode 100644 index 000000000000..10945fe12d50 --- /dev/null +++ b/drivers/hid/hid-petalynx.c @@ -0,0 +1,122 @@ +/* + * HID driver for some petalynx "special" devices + * + * 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) 2006-2007 Jiri Kosina + * Copyright (c) 2007 Paul Walmsley + * Copyright (c) 2008 Jiri Slaby + */ + +/* + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the Free + * Software Foundation; either version 2 of the License, or (at your option) + * any later version. + */ + +#include <linux/device.h> +#include <linux/hid.h> +#include <linux/module.h> + +#include "hid-ids.h" + +/* Petalynx Maxter Remote has maximum for consumer page set too low */ +static void pl_report_fixup(struct hid_device *hdev, __u8 *rdesc, + unsigned int rsize) +{ + if (rsize >= 60 && rdesc[39] == 0x2a && rdesc[40] == 0xf5 && + rdesc[41] == 0x00 && rdesc[59] == 0x26 && + rdesc[60] == 0xf9 && rdesc[61] == 0x00) { + dev_info(&hdev->dev, "fixing up Petalynx Maxter Remote report " + "descriptor\n"); + rdesc[60] = 0xfa; + rdesc[40] = 0xfa; + } +} + +#define pl_map_key_clear(c) hid_map_usage_clear(hi, usage, bit, max, \ + EV_KEY, (c)) +static int pl_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) == HID_UP_LOGIVENDOR) { + switch (usage->hid & HID_USAGE) { + case 0x05a: pl_map_key_clear(KEY_TEXT); break; + case 0x05b: pl_map_key_clear(KEY_RED); break; + case 0x05c: pl_map_key_clear(KEY_GREEN); break; + case 0x05d: pl_map_key_clear(KEY_YELLOW); break; + case 0x05e: pl_map_key_clear(KEY_BLUE); break; + default: + return 0; + } + return 1; + } + + if ((usage->hid & HID_USAGE_PAGE) == HID_UP_CONSUMER) { + switch (usage->hid & HID_USAGE) { + case 0x0f6: pl_map_key_clear(KEY_NEXT); break; + case 0x0fa: pl_map_key_clear(KEY_BACK); break; + default: + return 0; + } + return 1; + } + + return 0; +} + +static int pl_probe(struct hid_device *hdev, const struct hid_device_id *id) +{ + int ret; + + hdev->quirks |= HID_QUIRK_NOGET; + + ret = hid_parse(hdev); + if (ret) { + dev_err(&hdev->dev, "parse failed\n"); + goto err_free; + } + + ret = hid_hw_start(hdev, HID_CONNECT_DEFAULT); + if (ret) { + dev_err(&hdev->dev, "hw start failed\n"); + goto err_free; + } + + return 0; +err_free: + return ret; +} + +static const struct hid_device_id pl_devices[] = { + { HID_USB_DEVICE(USB_VENDOR_ID_PETALYNX, USB_DEVICE_ID_PETALYNX_MAXTER_REMOTE) }, + { } +}; +MODULE_DEVICE_TABLE(hid, pl_devices); + +static struct hid_driver pl_driver = { + .name = "petalynx", + .id_table = pl_devices, + .report_fixup = pl_report_fixup, + .input_mapping = pl_input_mapping, + .probe = pl_probe, +}; + +static int pl_init(void) +{ + return hid_register_driver(&pl_driver); +} + +static void pl_exit(void) +{ + hid_unregister_driver(&pl_driver); +} + +module_init(pl_init); +module_exit(pl_exit); +MODULE_LICENSE("GPL"); + +HID_COMPAT_LOAD_DRIVER(petalynx); diff --git a/drivers/hid/usbhid/hid-plff.c b/drivers/hid/hid-pl.c index 9eb83cf9d22b..acd815586182 100644 --- a/drivers/hid/usbhid/hid-plff.c +++ b/drivers/hid/hid-pl.c @@ -9,7 +9,7 @@ * - contains two reports, one for each port (HID_QUIRK_MULTI_INPUT) * * 0e8f:0003 "GreenAsia Inc. USB Joystick " - * - tested with Köng Gaming gamepad + * - tested with K??ng Gaming gamepad * * Copyright (c) 2007 Anssi Hannula <anssi.hannula@gmail.com> */ @@ -38,7 +38,11 @@ #include <linux/input.h> #include <linux/usb.h> #include <linux/hid.h> -#include "usbhid.h" + +#include "hid-ids.h" + +#ifdef CONFIG_PANTHERLORD_FF +#include "usbhid/usbhid.h" struct plff_device { struct hid_report *report; @@ -66,7 +70,7 @@ static int hid_plff_play(struct input_dev *dev, void *data, return 0; } -int hid_plff_init(struct hid_device *hid) +static int plff_init(struct hid_device *hid) { struct plff_device *plff; struct hid_report *report; @@ -86,7 +90,7 @@ int hid_plff_init(struct hid_device *hid) currently unknown. */ if (list_empty(report_list)) { - printk(KERN_ERR "hid-plff: no output reports found\n"); + dev_err(&hid->dev, "no output reports found\n"); return -ENODEV; } @@ -95,18 +99,19 @@ int hid_plff_init(struct hid_device *hid) report_ptr = report_ptr->next; if (report_ptr == report_list) { - printk(KERN_ERR "hid-plff: required output report is missing\n"); + dev_err(&hid->dev, "required output report is " + "missing\n"); return -ENODEV; } report = list_entry(report_ptr, struct hid_report, list); if (report->maxfield < 1) { - printk(KERN_ERR "hid-plff: no fields in the report\n"); + dev_err(&hid->dev, "no fields in the report\n"); return -ENODEV; } if (report->field[0]->report_count < 4) { - printk(KERN_ERR "hid-plff: not enough values in the field\n"); + dev_err(&hid->dev, "not enough values in the field\n"); return -ENODEV; } @@ -132,8 +137,70 @@ int hid_plff_init(struct hid_device *hid) usbhid_submit_report(hid, plff->report, USB_DIR_OUT); } - printk(KERN_INFO "hid-plff: Force feedback for PantherLord/GreenAsia " + dev_info(&hid->dev, "Force feedback for PantherLord/GreenAsia " "devices by Anssi Hannula <anssi.hannula@gmail.com>\n"); return 0; } +#else +static inline int plff_init(struct hid_device *hid) +{ + return 0; +} +#endif + +static int pl_probe(struct hid_device *hdev, const struct hid_device_id *id) +{ + int ret; + + if (id->driver_data) + hdev->quirks |= HID_QUIRK_MULTI_INPUT; + + ret = hid_parse(hdev); + if (ret) { + dev_err(&hdev->dev, "parse failed\n"); + goto err; + } + + ret = hid_hw_start(hdev, HID_CONNECT_DEFAULT & ~HID_CONNECT_FF); + if (ret) { + dev_err(&hdev->dev, "hw start failed\n"); + goto err; + } + + plff_init(hdev); + + return 0; +err: + return ret; +} + +static const struct hid_device_id pl_devices[] = { + { HID_USB_DEVICE(USB_VENDOR_ID_GAMERON, USB_DEVICE_ID_GAMERON_DUAL_PSX_ADAPTOR), + .driver_data = 1 }, /* Twin USB Joystick */ + { HID_USB_DEVICE(USB_VENDOR_ID_GREENASIA, 0x0003), }, /* GreenAsia Inc. USB Joystick */ + { } +}; +MODULE_DEVICE_TABLE(hid, pl_devices); + +static struct hid_driver pl_driver = { + .name = "pantherlord", + .id_table = pl_devices, + .probe = pl_probe, +}; + +static int pl_init(void) +{ + return hid_register_driver(&pl_driver); +} + +static void pl_exit(void) +{ + hid_unregister_driver(&pl_driver); +} + +module_init(pl_init); +module_exit(pl_exit); +MODULE_LICENSE("GPL"); + +HID_COMPAT_LOAD_DRIVER(pantherlord); diff --git a/drivers/hid/hid-samsung.c b/drivers/hid/hid-samsung.c new file mode 100644 index 000000000000..15f3c0492450 --- /dev/null +++ b/drivers/hid/hid-samsung.c @@ -0,0 +1,100 @@ +/* + * HID driver for some samsung "special" devices + * + * 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) 2006-2007 Jiri Kosina + * Copyright (c) 2007 Paul Walmsley + * Copyright (c) 2008 Jiri Slaby + */ + +/* + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the Free + * Software Foundation; either version 2 of the License, or (at your option) + * any later version. + */ + +#include <linux/device.h> +#include <linux/hid.h> +#include <linux/module.h> + +#include "hid-ids.h" + +/* + * Samsung IrDA remote controller (reports as Cypress USB Mouse). + * + * Vendor specific report #4 has a size of 48 bit, + * and therefore is not accepted when inspecting the descriptors. + * As a workaround we reinterpret the report as: + * Variable type, count 6, size 8 bit, log. maximum 255 + * The burden to reconstruct the data is moved into user space. + */ +static void samsung_report_fixup(struct hid_device *hdev, __u8 *rdesc, + unsigned int rsize) +{ + if (rsize >= 182 && rdesc[175] == 0x25 && rdesc[176] == 0x40 && + rdesc[177] == 0x75 && rdesc[178] == 0x30 && + rdesc[179] == 0x95 && rdesc[180] == 0x01 && + rdesc[182] == 0x40) { + dev_info(&hdev->dev, "fixing up Samsung IrDA report " + "descriptor\n"); + rdesc[176] = 0xff; + rdesc[178] = 0x08; + rdesc[180] = 0x06; + rdesc[182] = 0x42; + } +} + +static int samsung_probe(struct hid_device *hdev, + const struct hid_device_id *id) +{ + int ret; + + ret = hid_parse(hdev); + if (ret) { + dev_err(&hdev->dev, "parse failed\n"); + goto err_free; + } + + ret = hid_hw_start(hdev, (HID_CONNECT_DEFAULT & ~HID_CONNECT_HIDINPUT) | + HID_CONNECT_HIDDEV_FORCE); + if (ret) { + dev_err(&hdev->dev, "hw start failed\n"); + goto err_free; + } + + return 0; +err_free: + return ret; +} + +static const struct hid_device_id samsung_devices[] = { + { HID_USB_DEVICE(USB_VENDOR_ID_SAMSUNG, USB_DEVICE_ID_SAMSUNG_IR_REMOTE) }, + { } +}; +MODULE_DEVICE_TABLE(hid, samsung_devices); + +static struct hid_driver samsung_driver = { + .name = "samsung", + .id_table = samsung_devices, + .report_fixup = samsung_report_fixup, + .probe = samsung_probe, +}; + +static int samsung_init(void) +{ + return hid_register_driver(&samsung_driver); +} + +static void samsung_exit(void) +{ + hid_unregister_driver(&samsung_driver); +} + +module_init(samsung_init); +module_exit(samsung_exit); +MODULE_LICENSE("GPL"); + +HID_COMPAT_LOAD_DRIVER(samsung); diff --git a/drivers/hid/hid-sony.c b/drivers/hid/hid-sony.c new file mode 100644 index 000000000000..3af8095a7de1 --- /dev/null +++ b/drivers/hid/hid-sony.c @@ -0,0 +1,110 @@ +/* + * HID driver for some sony "special" devices + * + * 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) 2006-2007 Jiri Kosina + * Copyright (c) 2007 Paul Walmsley + * Copyright (c) 2008 Jiri Slaby + */ + +/* + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the Free + * Software Foundation; either version 2 of the License, or (at your option) + * any later version. + */ + +#include <linux/device.h> +#include <linux/hid.h> +#include <linux/module.h> +#include <linux/usb.h> + +#include "hid-ids.h" + +/* + * Sending HID_REQ_GET_REPORT changes the operation mode of the ps3 controller + * to "operational". Without this, the ps3 controller will not report any + * events. + */ +static int sony_set_operational(struct hid_device *hdev) +{ + struct usb_interface *intf = to_usb_interface(hdev->dev.parent); + struct usb_device *dev = interface_to_usbdev(intf); + __u16 ifnum = intf->cur_altsetting->desc.bInterfaceNumber; + int ret; + char *buf = kmalloc(18, GFP_KERNEL); + + if (!buf) + return -ENOMEM; + + ret = usb_control_msg(dev, usb_rcvctrlpipe(dev, 0), + HID_REQ_GET_REPORT, + USB_DIR_IN | USB_TYPE_CLASS | + USB_RECIP_INTERFACE, + (3 << 8) | 0xf2, ifnum, buf, 17, + USB_CTRL_GET_TIMEOUT); + if (ret < 0) + dev_err(&hdev->dev, "can't set operational mode\n"); + + kfree(buf); + + return ret; +} + +static int sony_probe(struct hid_device *hdev, const struct hid_device_id *id) +{ + int ret; + + ret = hid_parse(hdev); + if (ret) { + dev_err(&hdev->dev, "parse failed\n"); + goto err_free; + } + + ret = hid_hw_start(hdev, HID_CONNECT_DEFAULT | + HID_CONNECT_HIDDEV_FORCE); + if (ret) { + dev_err(&hdev->dev, "hw start failed\n"); + goto err_free; + } + + ret = sony_set_operational(hdev); + if (ret) + goto err_stop; + + return 0; +err_stop: + hid_hw_stop(hdev); +err_free: + return ret; +} + +static const struct hid_device_id sony_devices[] = { + { HID_USB_DEVICE(USB_VENDOR_ID_SONY, USB_DEVICE_ID_SONY_PS3_CONTROLLER) }, + { } +}; +MODULE_DEVICE_TABLE(hid, sony_devices); + +static struct hid_driver sony_driver = { + .name = "sony", + .id_table = sony_devices, + .probe = sony_probe, +}; + +static int sony_init(void) +{ + return hid_register_driver(&sony_driver); +} + +static void sony_exit(void) +{ + hid_unregister_driver(&sony_driver); +} + +module_init(sony_init); +module_exit(sony_exit); +MODULE_LICENSE("GPL"); + +HID_COMPAT_LOAD_DRIVER(sony); diff --git a/drivers/hid/hid-sunplus.c b/drivers/hid/hid-sunplus.c new file mode 100644 index 000000000000..5ba68f7dbb78 --- /dev/null +++ b/drivers/hid/hid-sunplus.c @@ -0,0 +1,82 @@ +/* + * HID driver for some sunplus "special" devices + * + * 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) 2006-2007 Jiri Kosina + * Copyright (c) 2007 Paul Walmsley + * Copyright (c) 2008 Jiri Slaby + */ + +/* + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the Free + * Software Foundation; either version 2 of the License, or (at your option) + * any later version. + */ + +#include <linux/device.h> +#include <linux/hid.h> +#include <linux/module.h> + +#include "hid-ids.h" + +static void sp_report_fixup(struct hid_device *hdev, __u8 *rdesc, + unsigned int rsize) +{ + if (rsize >= 107 && rdesc[104] == 0x26 && rdesc[105] == 0x80 && + rdesc[106] == 0x03) { + dev_info(&hdev->dev, "fixing up Sunplus Wireless Desktop " + "report descriptor\n"); + rdesc[105] = rdesc[110] = 0x03; + rdesc[106] = rdesc[111] = 0x21; + } +} + +#define sp_map_key_clear(c) hid_map_usage_clear(hi, usage, bit, max, \ + EV_KEY, (c)) +static int sp_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) != HID_UP_CONSUMER) + return 0; + + switch (usage->hid & HID_USAGE) { + case 0x2003: sp_map_key_clear(KEY_ZOOMIN); break; + case 0x2103: sp_map_key_clear(KEY_ZOOMOUT); break; + default: + return 0; + } + return 1; +} + +static const struct hid_device_id sp_devices[] = { + { HID_USB_DEVICE(USB_VENDOR_ID_SUNPLUS, USB_DEVICE_ID_SUNPLUS_WDESKTOP) }, + { } +}; +MODULE_DEVICE_TABLE(hid, sp_devices); + +static struct hid_driver sp_driver = { + .name = "sunplus", + .id_table = sp_devices, + .report_fixup = sp_report_fixup, + .input_mapping = sp_input_mapping, +}; + +static int sp_init(void) +{ + return hid_register_driver(&sp_driver); +} + +static void sp_exit(void) +{ + hid_unregister_driver(&sp_driver); +} + +module_init(sp_init); +module_exit(sp_exit); +MODULE_LICENSE("GPL"); + +HID_COMPAT_LOAD_DRIVER(sunplus); diff --git a/drivers/hid/usbhid/hid-tmff.c b/drivers/hid/hid-tmff.c index 144578b1a00c..1b7cba0f7e1f 100644 --- a/drivers/hid/usbhid/hid-tmff.c +++ b/drivers/hid/hid-tmff.c @@ -27,23 +27,17 @@ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ +#include <linux/hid.h> #include <linux/input.h> - -#undef DEBUG #include <linux/usb.h> -#include <linux/hid.h> -#include "usbhid.h" +#include "hid-ids.h" + +#include "usbhid/usbhid.h" /* Usages for thrustmaster devices I know about */ #define THRUSTMASTER_USAGE_FF (HID_UP_GENDESK | 0xbb) -struct dev_type { - u16 idVendor; - u16 idProduct; - const signed short *ff; -}; - static const signed short ff_rumble[] = { FF_RUMBLE, -1 @@ -54,21 +48,13 @@ static const signed short ff_joystick[] = { -1 }; -static const struct dev_type devices[] = { - { 0x44f, 0xb300, ff_rumble }, - { 0x44f, 0xb304, ff_rumble }, - { 0x44f, 0xb651, ff_rumble }, /* FGT Rumble Force Wheel */ - { 0x44f, 0xb654, ff_joystick }, /* FGT Force Feedback Wheel */ -}; - struct tmff_device { struct hid_report *report; struct hid_field *ff_field; }; /* Changes values from 0 to 0xffff into values from minimum to maximum */ -static inline int hid_tmff_scale_u16(unsigned int in, - int minimum, int maximum) +static inline int tmff_scale_u16(unsigned int in, int minimum, int maximum) { int ret; @@ -81,8 +67,7 @@ static inline int hid_tmff_scale_u16(unsigned int in, } /* Changes values from -0x80 to 0x7f into values from minimum to maximum */ -static inline int hid_tmff_scale_s8(int in, - int minimum, int maximum) +static inline int tmff_scale_s8(int in, int minimum, int maximum) { int ret; @@ -94,7 +79,8 @@ static inline int hid_tmff_scale_s8(int in, return ret; } -static int hid_tmff_play(struct input_dev *dev, void *data, struct ff_effect *effect) +static int tmff_play(struct input_dev *dev, void *data, + struct ff_effect *effect) { struct hid_device *hid = input_get_drvdata(dev); struct tmff_device *tmff = data; @@ -104,10 +90,10 @@ static int hid_tmff_play(struct input_dev *dev, void *data, struct ff_effect *ef switch (effect->type) { case FF_CONSTANT: - x = hid_tmff_scale_s8(effect->u.ramp.start_level, + x = tmff_scale_s8(effect->u.ramp.start_level, ff_field->logical_minimum, ff_field->logical_maximum); - y = hid_tmff_scale_s8(effect->u.ramp.end_level, + y = tmff_scale_s8(effect->u.ramp.end_level, ff_field->logical_minimum, ff_field->logical_maximum); @@ -118,10 +104,10 @@ static int hid_tmff_play(struct input_dev *dev, void *data, struct ff_effect *ef break; case FF_RUMBLE: - left = hid_tmff_scale_u16(effect->u.rumble.weak_magnitude, + left = tmff_scale_u16(effect->u.rumble.weak_magnitude, ff_field->logical_minimum, ff_field->logical_maximum); - right = hid_tmff_scale_u16(effect->u.rumble.strong_magnitude, + right = tmff_scale_u16(effect->u.rumble.strong_magnitude, ff_field->logical_minimum, ff_field->logical_maximum); @@ -134,14 +120,14 @@ static int hid_tmff_play(struct input_dev *dev, void *data, struct ff_effect *ef return 0; } -int hid_tmff_init(struct hid_device *hid) +static int tmff_init(struct hid_device *hid, const signed short *ff_bits) { struct tmff_device *tmff; struct hid_report *report; struct list_head *report_list; - struct hid_input *hidinput = list_entry(hid->inputs.next, struct hid_input, list); + struct hid_input *hidinput = list_entry(hid->inputs.next, + struct hid_input, list); struct input_dev *input_dev = hidinput->input; - const signed short *ff_bits = ff_joystick; int error; int i; @@ -163,63 +149,121 @@ int hid_tmff_init(struct hid_device *hid) switch (field->usage[0].hid) { case THRUSTMASTER_USAGE_FF: if (field->report_count < 2) { - warn("ignoring FF field with report_count < 2"); + dev_warn(&hid->dev, "ignoring FF field " + "with report_count < 2\n"); continue; } - if (field->logical_maximum == field->logical_minimum) { - warn("ignoring FF field with logical_maximum == logical_minimum"); + if (field->logical_maximum == + field->logical_minimum) { + dev_warn(&hid->dev, "ignoring FF field " + "with logical_maximum " + "== logical_minimum\n"); continue; } if (tmff->report && tmff->report != report) { - warn("ignoring FF field in other report"); + dev_warn(&hid->dev, "ignoring FF field " + "in other report\n"); continue; } if (tmff->ff_field && tmff->ff_field != field) { - warn("ignoring duplicate FF field"); + dev_warn(&hid->dev, "ignoring " + "duplicate FF field\n"); continue; } tmff->report = report; tmff->ff_field = field; - for (i = 0; i < ARRAY_SIZE(devices); i++) { - if (input_dev->id.vendor == devices[i].idVendor && - input_dev->id.product == devices[i].idProduct) { - ff_bits = devices[i].ff; - break; - } - } - for (i = 0; ff_bits[i] >= 0; i++) set_bit(ff_bits[i], input_dev->ffbit); break; default: - warn("ignoring unknown output usage %08x", field->usage[0].hid); + dev_warn(&hid->dev, "ignoring unknown output " + "usage %08x\n", + field->usage[0].hid); continue; } } } if (!tmff->report) { - err("cant find FF field in output reports\n"); + dev_err(&hid->dev, "can't find FF field in output reports\n"); error = -ENODEV; goto fail; } - error = input_ff_create_memless(input_dev, tmff, hid_tmff_play); + error = input_ff_create_memless(input_dev, tmff, tmff_play); if (error) goto fail; - info("Force feedback for ThrustMaster devices by Zinx Verituse <zinx@epicsol.org>"); + dev_info(&hid->dev, "force feedback for ThrustMaster devices by Zinx " + "Verituse <zinx@epicsol.org>"); return 0; - fail: +fail: kfree(tmff); return error; } +static int tm_probe(struct hid_device *hdev, const struct hid_device_id *id) +{ + int ret; + + ret = hid_parse(hdev); + if (ret) { + dev_err(&hdev->dev, "parse failed\n"); + goto err; + } + + ret = hid_hw_start(hdev, HID_CONNECT_DEFAULT & ~HID_CONNECT_FF); + if (ret) { + dev_err(&hdev->dev, "hw start failed\n"); + goto err; + } + + tmff_init(hdev, (void *)id->driver_data); + + return 0; +err: + return ret; +} + +static const struct hid_device_id tm_devices[] = { + { HID_USB_DEVICE(USB_VENDOR_ID_THRUSTMASTER, 0xb300), + .driver_data = (unsigned long)ff_rumble }, + { HID_USB_DEVICE(USB_VENDOR_ID_THRUSTMASTER, 0xb304), + .driver_data = (unsigned long)ff_rumble }, + { HID_USB_DEVICE(USB_VENDOR_ID_THRUSTMASTER, 0xb651), /* FGT Rumble Force Wheel */ + .driver_data = (unsigned long)ff_rumble }, + { HID_USB_DEVICE(USB_VENDOR_ID_THRUSTMASTER, 0xb654), /* FGT Force Feedback Wheel */ + .driver_data = (unsigned long)ff_joystick }, + { } +}; +MODULE_DEVICE_TABLE(hid, tm_devices); + +static struct hid_driver tm_driver = { + .name = "thrustmaster", + .id_table = tm_devices, + .probe = tm_probe, +}; + +static int tm_init(void) +{ + return hid_register_driver(&tm_driver); +} + +static void tm_exit(void) +{ + hid_unregister_driver(&tm_driver); +} + +module_init(tm_init); +module_exit(tm_exit); +MODULE_LICENSE("GPL"); + +HID_COMPAT_LOAD_DRIVER(thrustmaster); diff --git a/drivers/hid/usbhid/hid-zpff.c b/drivers/hid/hid-zpff.c index 5a688274f6a3..ea82f3718b21 100644 --- a/drivers/hid/usbhid/hid-zpff.c +++ b/drivers/hid/hid-zpff.c @@ -21,16 +21,19 @@ */ +#include <linux/hid.h> #include <linux/input.h> #include <linux/usb.h> -#include <linux/hid.h> -#include "usbhid.h" + +#include "hid-ids.h" + +#include "usbhid/usbhid.h" struct zpff_device { struct hid_report *report; }; -static int hid_zpff_play(struct input_dev *dev, void *data, +static int zpff_play(struct input_dev *dev, void *data, struct ff_effect *effect) { struct hid_device *hid = input_get_drvdata(dev); @@ -58,7 +61,7 @@ static int hid_zpff_play(struct input_dev *dev, void *data, return 0; } -int hid_zpff_init(struct hid_device *hid) +static int zpff_init(struct hid_device *hid) { struct zpff_device *zpff; struct hid_report *report; @@ -70,14 +73,14 @@ int hid_zpff_init(struct hid_device *hid) int error; if (list_empty(report_list)) { - printk(KERN_ERR "hid-zpff: no output report found\n"); + dev_err(&hid->dev, "no output report found\n"); return -ENODEV; } report = list_entry(report_list->next, struct hid_report, list); if (report->maxfield < 4) { - printk(KERN_ERR "hid-zpff: not enough fields in report\n"); + dev_err(&hid->dev, "not enough fields in report\n"); return -ENODEV; } @@ -87,7 +90,7 @@ int hid_zpff_init(struct hid_device *hid) set_bit(FF_RUMBLE, dev->ffbit); - error = input_ff_create_memless(dev, zpff, hid_zpff_play); + error = input_ff_create_memless(dev, zpff, zpff_play); if (error) { kfree(zpff); return error; @@ -100,8 +103,60 @@ int hid_zpff_init(struct hid_device *hid) zpff->report->field[3]->value[0] = 0x00; usbhid_submit_report(hid, zpff->report, USB_DIR_OUT); - printk(KERN_INFO "Force feedback for Zeroplus based devices by " + dev_info(&hid->dev, "force feedback for Zeroplus based devices by " "Anssi Hannula <anssi.hannula@gmail.com>\n"); return 0; } + +static int zp_probe(struct hid_device *hdev, const struct hid_device_id *id) +{ + int ret; + + ret = hid_parse(hdev); + if (ret) { + dev_err(&hdev->dev, "parse failed\n"); + goto err; + } + + ret = hid_hw_start(hdev, HID_CONNECT_DEFAULT & ~HID_CONNECT_FF); + if (ret) { + dev_err(&hdev->dev, "hw start failed\n"); + goto err; + } + + zpff_init(hdev); + + return 0; +err: + return ret; +} + +static const struct hid_device_id zp_devices[] = { + { HID_USB_DEVICE(USB_VENDOR_ID_ZEROPLUS, 0x0005) }, + { HID_USB_DEVICE(USB_VENDOR_ID_ZEROPLUS, 0x0030) }, + { } +}; +MODULE_DEVICE_TABLE(hid, zp_devices); + +static struct hid_driver zp_driver = { + .name = "zeroplus", + .id_table = zp_devices, + .probe = zp_probe, +}; + +static int zp_init(void) +{ + return hid_register_driver(&zp_driver); +} + +static void zp_exit(void) +{ + hid_unregister_driver(&zp_driver); +} + +module_init(zp_init); +module_exit(zp_exit); +MODULE_LICENSE("GPL"); + +HID_COMPAT_LOAD_DRIVER(zeroplus); diff --git a/drivers/hid/hidraw.c b/drivers/hid/hidraw.c index c40f0403edaf..497e0d1dd3c3 100644 --- a/drivers/hid/hidraw.c +++ b/drivers/hid/hidraw.c @@ -113,7 +113,7 @@ static ssize_t hidraw_write(struct file *file, const char __user *buffer, size_t if (!dev->hid_output_raw_report) return -ENODEV; - if (count > HID_MIN_BUFFER_SIZE) { + if (count > HID_MAX_BUFFER_SIZE) { printk(KERN_WARNING "hidraw: pid %d passed too large report\n", task_pid_nr(current)); return -EINVAL; @@ -181,7 +181,7 @@ static int hidraw_open(struct inode *inode, struct file *file) dev = hidraw_table[minor]; if (!dev->open++) - dev->hid->hid_open(dev->hid); + dev->hid->ll_driver->open(dev->hid); out_unlock: spin_unlock(&minors_lock); @@ -207,7 +207,7 @@ static int hidraw_release(struct inode * inode, struct file * file) dev = hidraw_table[minor]; if (!dev->open--) { if (list->hidraw->exist) - dev->hid->hid_close(dev->hid); + dev->hid->ll_driver->close(dev->hid); else kfree(list->hidraw); } @@ -367,7 +367,7 @@ void hidraw_disconnect(struct hid_device *hid) device_destroy(hidraw_class, MKDEV(hidraw_major, hidraw->minor)); if (hidraw->open) { - hid->hid_close(hid); + hid->ll_driver->close(hid); wake_up_interruptible(&hidraw->wait); } else { kfree(hidraw); diff --git a/drivers/hid/usbhid/Kconfig b/drivers/hid/usbhid/Kconfig index 18f09104765c..5d9aa95fc3ef 100644 --- a/drivers/hid/usbhid/Kconfig +++ b/drivers/hid/usbhid/Kconfig @@ -24,88 +24,13 @@ config USB_HID comment "Input core support is needed for USB HID input layer or HIDBP support" depends on USB_HID && INPUT=n -config USB_HIDINPUT_POWERBOOK - bool "Enable support for Apple laptop/aluminum USB special keys" - default n - depends on USB_HID - help - Say Y here if you want support for the special keys (Fn, Numlock) on - Apple iBooks, PowerBooks, MacBooks, MacBook Pros and aluminum USB - keyboards. - - If unsure, say N. - -config HID_FF - bool "Force feedback support (EXPERIMENTAL)" - depends on USB_HID && EXPERIMENTAL - help - Say Y here is you want force feedback support for a few HID devices. - See below for a list of supported devices. - - See <file:Documentation/input/ff.txt> for a description of the force - feedback API. - - If unsure, say N. - config HID_PID bool "PID device support" - depends on HID_FF help Say Y here if you have a PID-compliant device and wish to enable force feedback for it. Microsoft Sidewinder Force Feedback 2 is one of such devices. -config LOGITECH_FF - bool "Logitech devices support" - depends on HID_FF - select INPUT_FF_MEMLESS if USB_HID - help - Say Y here if you have one of these devices: - - Logitech WingMan Cordless RumblePad - - Logitech WingMan Cordless RumblePad 2 - - Logitech WingMan Force 3D - - Logitech Formula Force EX - - Logitech MOMO Force wheel - - and if you want to enable force feedback for them. - Note: if you say N here, this device will still be supported, but without - force feedback. - -config LOGIRUMBLEPAD2_FF - bool "Logitech Rumblepad 2 support" - depends on HID_FF - select INPUT_FF_MEMLESS if USB_HID - help - Say Y here if you want to enable force feedback support for Logitech - Rumblepad 2 devices. - -config PANTHERLORD_FF - bool "PantherLord/GreenAsia based device support" - depends on HID_FF - select INPUT_FF_MEMLESS if USB_HID - help - Say Y here if you have a PantherLord/GreenAsia based game controller - or adapter and want to enable force feedback support for it. - -config THRUSTMASTER_FF - bool "ThrustMaster devices support" - depends on HID_FF - select INPUT_FF_MEMLESS if USB_HID - help - Say Y here if you have a THRUSTMASTER FireStore Dual Power 2 or - a THRUSTMASTER Ferrari GT Rumble Force or Force Feedback Wheel, - and want to enable force feedback support for it. - Note: if you say N here, this device will still be supported, but without - force feedback. - -config ZEROPLUS_FF - bool "Zeroplus based game controller support" - depends on HID_FF - select INPUT_FF_MEMLESS if USB_HID - help - Say Y here if you have a Zeroplus based game controller and want to - enable force feedback for it. - config USB_HIDDEV bool "/dev/hiddev raw HID device support" depends on USB_HID diff --git a/drivers/hid/usbhid/Makefile b/drivers/hid/usbhid/Makefile index 00a7b7090192..1329ecb37a1c 100644 --- a/drivers/hid/usbhid/Makefile +++ b/drivers/hid/usbhid/Makefile @@ -13,24 +13,6 @@ endif ifeq ($(CONFIG_HID_PID),y) usbhid-objs += hid-pidff.o endif -ifeq ($(CONFIG_LOGITECH_FF),y) - usbhid-objs += hid-lgff.o -endif -ifeq ($(CONFIG_LOGIRUMBLEPAD2_FF),y) - usbhid-objs += hid-lg2ff.o -endif -ifeq ($(CONFIG_PANTHERLORD_FF),y) - usbhid-objs += hid-plff.o -endif -ifeq ($(CONFIG_THRUSTMASTER_FF),y) - usbhid-objs += hid-tmff.o -endif -ifeq ($(CONFIG_ZEROPLUS_FF),y) - usbhid-objs += hid-zpff.o -endif -ifeq ($(CONFIG_HID_FF),y) - usbhid-objs += hid-ff.o -endif obj-$(CONFIG_USB_HID) += usbhid.o obj-$(CONFIG_USB_KBD) += usbkbd.o diff --git a/drivers/hid/usbhid/hid-core.c b/drivers/hid/usbhid/hid-core.c index 27fe4d8912cb..1d3b8a394d46 100644 --- a/drivers/hid/usbhid/hid-core.c +++ b/drivers/hid/usbhid/hid-core.c @@ -44,8 +44,6 @@ #define DRIVER_DESC "USB HID core driver" #define DRIVER_LICENSE "GPL" -static char *hid_types[] = {"Device", "Pointer", "Mouse", "Device", "Joystick", - "Gamepad", "Keyboard", "Keypad", "Multi-Axis Controller"}; /* * Module parameters. */ @@ -61,12 +59,6 @@ MODULE_PARM_DESC(quirks, "Add/modify USB HID quirks by specifying " " quirks=vendorID:productID:quirks" " where vendorID, productID, and quirks are all in" " 0x-prefixed hex"); -static char *rdesc_quirks_param[MAX_USBHID_BOOT_QUIRKS] = { [ 0 ... (MAX_USBHID_BOOT_QUIRKS - 1) ] = NULL }; -module_param_array_named(rdesc_quirks, rdesc_quirks_param, charp, NULL, 0444); -MODULE_PARM_DESC(rdesc_quirks, "Add/modify report descriptor quirks by specifying " - " rdesc_quirks=vendorID:productID:rdesc_quirks" - " where vendorID, productID, and rdesc_quirks are all in" - " 0x-prefixed hex"); /* * Input submission and I/O error handler. */ @@ -197,31 +189,32 @@ static void hid_irq_in(struct urb *urb) int status; switch (urb->status) { - case 0: /* success */ - usbhid->retry_delay = 0; - hid_input_report(urb->context, HID_INPUT_REPORT, - urb->transfer_buffer, - urb->actual_length, 1); - break; - case -EPIPE: /* stall */ - clear_bit(HID_IN_RUNNING, &usbhid->iofl); - set_bit(HID_CLEAR_HALT, &usbhid->iofl); - schedule_work(&usbhid->reset_work); - return; - case -ECONNRESET: /* unlink */ - case -ENOENT: - case -ESHUTDOWN: /* unplug */ - clear_bit(HID_IN_RUNNING, &usbhid->iofl); - return; - case -EILSEQ: /* protocol error or unplug */ - case -EPROTO: /* protocol error or unplug */ - case -ETIME: /* protocol error or unplug */ - case -ETIMEDOUT: /* Should never happen, but... */ - clear_bit(HID_IN_RUNNING, &usbhid->iofl); - hid_io_error(hid); - return; - default: /* error */ - warn("input irq status %d received", urb->status); + case 0: /* success */ + usbhid->retry_delay = 0; + hid_input_report(urb->context, HID_INPUT_REPORT, + urb->transfer_buffer, + urb->actual_length, 1); + break; + case -EPIPE: /* stall */ + clear_bit(HID_IN_RUNNING, &usbhid->iofl); + set_bit(HID_CLEAR_HALT, &usbhid->iofl); + schedule_work(&usbhid->reset_work); + return; + case -ECONNRESET: /* unlink */ + case -ENOENT: + case -ESHUTDOWN: /* unplug */ + clear_bit(HID_IN_RUNNING, &usbhid->iofl); + return; + case -EILSEQ: /* protocol error or unplug */ + case -EPROTO: /* protocol error or unplug */ + case -ETIME: /* protocol error or unplug */ + case -ETIMEDOUT: /* Should never happen, but... */ + clear_bit(HID_IN_RUNNING, &usbhid->iofl); + hid_io_error(hid); + return; + default: /* error */ + dev_warn(&urb->dev->dev, "input irq status %d " + "received\n", urb->status); } status = usb_submit_urb(urb, GFP_ATOMIC); @@ -240,13 +233,16 @@ static void hid_irq_in(struct urb *urb) static int hid_submit_out(struct hid_device *hid) { struct hid_report *report; + char *raw_report; struct usbhid_device *usbhid = hid->driver_data; - report = usbhid->out[usbhid->outtail]; + report = usbhid->out[usbhid->outtail].report; + raw_report = usbhid->out[usbhid->outtail].raw_report; - hid_output_report(report, usbhid->outbuf); usbhid->urbout->transfer_buffer_length = ((report->size - 1) >> 3) + 1 + (report->id > 0); usbhid->urbout->dev = hid_to_usb_dev(hid); + memcpy(usbhid->outbuf, raw_report, usbhid->urbout->transfer_buffer_length); + kfree(raw_report); dbg_hid("submitting out urb\n"); @@ -262,17 +258,20 @@ static int hid_submit_ctrl(struct hid_device *hid) { struct hid_report *report; unsigned char dir; + char *raw_report; int len; struct usbhid_device *usbhid = hid->driver_data; report = usbhid->ctrl[usbhid->ctrltail].report; + raw_report = usbhid->ctrl[usbhid->ctrltail].raw_report; dir = usbhid->ctrl[usbhid->ctrltail].dir; len = ((report->size - 1) >> 3) + 1 + (report->id > 0); if (dir == USB_DIR_OUT) { - hid_output_report(report, usbhid->ctrlbuf); usbhid->urbctrl->pipe = usb_sndctrlpipe(hid_to_usb_dev(hid), 0); usbhid->urbctrl->transfer_buffer_length = len; + memcpy(usbhid->ctrlbuf, raw_report, len); + kfree(raw_report); } else { int maxpacket, padlen; @@ -319,17 +318,18 @@ static void hid_irq_out(struct urb *urb) int unplug = 0; switch (urb->status) { - case 0: /* success */ - break; - case -ESHUTDOWN: /* unplug */ - unplug = 1; - case -EILSEQ: /* protocol error or unplug */ - case -EPROTO: /* protocol error or unplug */ - case -ECONNRESET: /* unlink */ - case -ENOENT: - break; - default: /* error */ - warn("output irq status %d received", urb->status); + case 0: /* success */ + break; + case -ESHUTDOWN: /* unplug */ + unplug = 1; + case -EILSEQ: /* protocol error or unplug */ + case -EPROTO: /* protocol error or unplug */ + case -ECONNRESET: /* unlink */ + case -ENOENT: + break; + default: /* error */ + dev_warn(&urb->dev->dev, "output irq status %d " + "received\n", urb->status); } spin_lock_irqsave(&usbhid->outlock, flags); @@ -367,21 +367,23 @@ static void hid_ctrl(struct urb *urb) spin_lock_irqsave(&usbhid->ctrllock, flags); switch (urb->status) { - case 0: /* success */ - if (usbhid->ctrl[usbhid->ctrltail].dir == USB_DIR_IN) - hid_input_report(urb->context, usbhid->ctrl[usbhid->ctrltail].report->type, - urb->transfer_buffer, urb->actual_length, 0); - break; - case -ESHUTDOWN: /* unplug */ - unplug = 1; - case -EILSEQ: /* protocol error or unplug */ - case -EPROTO: /* protocol error or unplug */ - case -ECONNRESET: /* unlink */ - case -ENOENT: - case -EPIPE: /* report not available */ - break; - default: /* error */ - warn("ctrl urb status %d received", urb->status); + case 0: /* success */ + if (usbhid->ctrl[usbhid->ctrltail].dir == USB_DIR_IN) + hid_input_report(urb->context, + usbhid->ctrl[usbhid->ctrltail].report->type, + urb->transfer_buffer, urb->actual_length, 0); + break; + case -ESHUTDOWN: /* unplug */ + unplug = 1; + case -EILSEQ: /* protocol error or unplug */ + case -EPROTO: /* protocol error or unplug */ + case -ECONNRESET: /* unlink */ + case -ENOENT: + case -EPIPE: /* report not available */ + break; + default: /* error */ + dev_warn(&urb->dev->dev, "ctrl urb status %d " + "received\n", urb->status); } if (unplug) @@ -408,6 +410,7 @@ void usbhid_submit_report(struct hid_device *hid, struct hid_report *report, uns int head; unsigned long flags; struct usbhid_device *usbhid = hid->driver_data; + int len = ((report->size - 1) >> 3) + 1 + (report->id > 0); if ((hid->quirks & HID_QUIRK_NOGET) && dir == USB_DIR_IN) return; @@ -418,11 +421,18 @@ void usbhid_submit_report(struct hid_device *hid, struct hid_report *report, uns if ((head = (usbhid->outhead + 1) & (HID_OUTPUT_FIFO_SIZE - 1)) == usbhid->outtail) { spin_unlock_irqrestore(&usbhid->outlock, flags); - warn("output queue full"); + dev_warn(&hid->dev, "output queue full\n"); return; } - usbhid->out[usbhid->outhead] = report; + usbhid->out[usbhid->outhead].raw_report = kmalloc(len, GFP_ATOMIC); + if (!usbhid->out[usbhid->outhead].raw_report) { + spin_unlock_irqrestore(&usbhid->outlock, flags); + warn("output queueing failed"); + return; + } + hid_output_report(report, usbhid->out[usbhid->outhead].raw_report); + usbhid->out[usbhid->outhead].report = report; usbhid->outhead = head; if (!test_and_set_bit(HID_OUT_RUNNING, &usbhid->iofl)) @@ -437,10 +447,19 @@ void usbhid_submit_report(struct hid_device *hid, struct hid_report *report, uns if ((head = (usbhid->ctrlhead + 1) & (HID_CONTROL_FIFO_SIZE - 1)) == usbhid->ctrltail) { spin_unlock_irqrestore(&usbhid->ctrllock, flags); - warn("control queue full"); + dev_warn(&hid->dev, "control queue full\n"); return; } + if (dir == USB_DIR_OUT) { + usbhid->ctrl[usbhid->ctrlhead].raw_report = kmalloc(len, GFP_ATOMIC); + if (!usbhid->ctrl[usbhid->ctrlhead].raw_report) { + spin_unlock_irqrestore(&usbhid->ctrllock, flags); + warn("control queueing failed"); + return; + } + hid_output_report(report, usbhid->ctrl[usbhid->ctrlhead].raw_report); + } usbhid->ctrl[usbhid->ctrlhead].report = report; usbhid->ctrl[usbhid->ctrlhead].dir = dir; usbhid->ctrlhead = head; @@ -451,6 +470,7 @@ void usbhid_submit_report(struct hid_device *hid, struct hid_report *report, uns spin_unlock_irqrestore(&usbhid->ctrllock, flags); } +EXPORT_SYMBOL_GPL(usbhid_submit_report); static int usb_hidinput_input_event(struct input_dev *dev, unsigned int type, unsigned int code, int value) { @@ -465,7 +485,7 @@ static int usb_hidinput_input_event(struct input_dev *dev, unsigned int type, un return -1; if ((offset = hidinput_find_field(hid, type, code, &field)) == -1) { - warn("event field not found"); + dev_warn(&dev->dev, "event field not found\n"); return -1; } @@ -568,7 +588,7 @@ void usbhid_init_reports(struct hid_device *hid) } if (err) - warn("timeout initializing reports"); + dev_warn(&hid->dev, "timeout initializing reports\n"); } /* @@ -598,7 +618,7 @@ static int hid_find_field_early(struct hid_device *hid, unsigned int page, return -1; } -static void usbhid_set_leds(struct hid_device *hid) +void usbhid_set_leds(struct hid_device *hid) { struct hid_field *field; int offset; @@ -608,6 +628,7 @@ static void usbhid_set_leds(struct hid_device *hid) usbhid_submit_report(hid, field->report, USB_DIR_OUT); } } +EXPORT_SYMBOL_GPL(usbhid_set_leds); /* * Traverse the supplied list of reports and find the longest @@ -675,43 +696,16 @@ static void hid_free_buffers(struct usb_device *dev, struct hid_device *hid) usb_buffer_free(dev, usbhid->bufsize, usbhid->ctrlbuf, usbhid->ctrlbuf_dma); } -/* - * Sending HID_REQ_GET_REPORT changes the operation mode of the ps3 controller - * to "operational". Without this, the ps3 controller will not report any - * events. - */ -static void hid_fixup_sony_ps3_controller(struct usb_device *dev, int ifnum) -{ - int result; - char *buf = kmalloc(18, GFP_KERNEL); - - if (!buf) - return; - - result = usb_control_msg(dev, usb_rcvctrlpipe(dev, 0), - HID_REQ_GET_REPORT, - USB_DIR_IN | USB_TYPE_CLASS | - USB_RECIP_INTERFACE, - (3 << 8) | 0xf2, ifnum, buf, 17, - USB_CTRL_GET_TIMEOUT); - - if (result < 0) - err_hid("%s failed: %d\n", __func__, result); - - kfree(buf); -} - -static struct hid_device *usb_hid_configure(struct usb_interface *intf) +static int usbhid_parse(struct hid_device *hid) { + struct usb_interface *intf = to_usb_interface(hid->dev.parent); struct usb_host_interface *interface = intf->cur_altsetting; struct usb_device *dev = interface_to_usbdev (intf); struct hid_descriptor *hdesc; - struct hid_device *hid; u32 quirks = 0; - unsigned int insize = 0, rsize = 0; + unsigned int rsize = 0; char *rdesc; - int n, len; - struct usbhid_device *usbhid; + int ret, n; quirks = usbhid_lookup_quirk(le16_to_cpu(dev->descriptor.idVendor), le16_to_cpu(dev->descriptor.idProduct)); @@ -724,63 +718,75 @@ static struct hid_device *usb_hid_configure(struct usb_interface *intf) quirks |= HID_QUIRK_NOGET; } - if (quirks & HID_QUIRK_IGNORE) - return NULL; - - if ((quirks & HID_QUIRK_IGNORE_MOUSE) && - (interface->desc.bInterfaceProtocol == USB_INTERFACE_PROTOCOL_MOUSE)) - return NULL; - - if (usb_get_extra_descriptor(interface, HID_DT_HID, &hdesc) && (!interface->desc.bNumEndpoints || usb_get_extra_descriptor(&interface->endpoint[0], HID_DT_HID, &hdesc))) { dbg_hid("class descriptor not present\n"); - return NULL; + return -ENODEV; } + hid->version = le16_to_cpu(hdesc->bcdHID); + hid->country = hdesc->bCountryCode; + for (n = 0; n < hdesc->bNumDescriptors; n++) if (hdesc->desc[n].bDescriptorType == HID_DT_REPORT) rsize = le16_to_cpu(hdesc->desc[n].wDescriptorLength); if (!rsize || rsize > HID_MAX_DESCRIPTOR_SIZE) { dbg_hid("weird size of report descriptor (%u)\n", rsize); - return NULL; + return -EINVAL; } if (!(rdesc = kmalloc(rsize, GFP_KERNEL))) { dbg_hid("couldn't allocate rdesc memory\n"); - return NULL; + return -ENOMEM; } hid_set_idle(dev, interface->desc.bInterfaceNumber, 0, 0); - if ((n = hid_get_class_descriptor(dev, interface->desc.bInterfaceNumber, HID_DT_REPORT, rdesc, rsize)) < 0) { + ret = hid_get_class_descriptor(dev, interface->desc.bInterfaceNumber, + HID_DT_REPORT, rdesc, rsize); + if (ret < 0) { dbg_hid("reading report descriptor failed\n"); kfree(rdesc); - return NULL; + goto err; } - usbhid_fixup_report_descriptor(le16_to_cpu(dev->descriptor.idVendor), - le16_to_cpu(dev->descriptor.idProduct), rdesc, - rsize, rdesc_quirks_param); - dbg_hid("report descriptor (size %u, read %d) = ", rsize, n); for (n = 0; n < rsize; n++) dbg_hid_line(" %02x", (unsigned char) rdesc[n]); dbg_hid_line("\n"); - if (!(hid = hid_parse_report(rdesc, n))) { + ret = hid_parse_report(hid, rdesc, rsize); + kfree(rdesc); + if (ret) { dbg_hid("parsing report descriptor failed\n"); - kfree(rdesc); - return NULL; + goto err; } - kfree(rdesc); hid->quirks = quirks; - if (!(usbhid = kzalloc(sizeof(struct usbhid_device), GFP_KERNEL))) - goto fail_no_usbhid; + return 0; +err: + return ret; +} + +static int usbhid_start(struct hid_device *hid) +{ + struct usb_interface *intf = to_usb_interface(hid->dev.parent); + struct usb_host_interface *interface = intf->cur_altsetting; + struct usb_device *dev = interface_to_usbdev(intf); + struct usbhid_device *usbhid; + unsigned int n, insize = 0; + int ret; + + WARN_ON(hid->driver_data); + + usbhid = kzalloc(sizeof(struct usbhid_device), GFP_KERNEL); + if (usbhid == NULL) { + ret = -ENOMEM; + goto err; + } hid->driver_data = usbhid; usbhid->hid = hid; @@ -799,28 +805,11 @@ static struct hid_device *usb_hid_configure(struct usb_interface *intf) insize = HID_MAX_BUFFER_SIZE; if (hid_alloc_buffers(dev, hid)) { - hid_free_buffers(dev, hid); + ret = -ENOMEM; goto fail; } - hid->name[0] = 0; - - if (dev->manufacturer) - strlcpy(hid->name, dev->manufacturer, sizeof(hid->name)); - - if (dev->product) { - if (dev->manufacturer) - strlcat(hid->name, " ", sizeof(hid->name)); - strlcat(hid->name, dev->product, sizeof(hid->name)); - } - - if (!strlen(hid->name)) - snprintf(hid->name, sizeof(hid->name), "HID %04x:%04x", - le16_to_cpu(dev->descriptor.idVendor), - le16_to_cpu(dev->descriptor.idProduct)); - for (n = 0; n < interface->desc.bNumEndpoints; n++) { - struct usb_endpoint_descriptor *endpoint; int pipe; int interval; @@ -832,7 +821,7 @@ static struct hid_device *usb_hid_configure(struct usb_interface *intf) interval = endpoint->bInterval; /* Some vendors give fullspeed interval on highspeed devides */ - if (quirks & HID_QUIRK_FULLSPEED_INTERVAL && + if (hid->quirks & HID_QUIRK_FULLSPEED_INTERVAL && dev->speed == USB_SPEED_HIGH) { interval = fls(endpoint->bInterval*8); printk(KERN_INFO "%s: Fixing fullspeed to highspeed interval: %d -> %d\n", @@ -843,6 +832,7 @@ static struct hid_device *usb_hid_configure(struct usb_interface *intf) if (hid->collection->usage == HID_GD_MOUSE && hid_mousepoll_interval > 0) interval = hid_mousepoll_interval; + ret = -ENOMEM; if (usb_endpoint_dir_in(endpoint)) { if (usbhid->urbin) continue; @@ -868,6 +858,7 @@ static struct hid_device *usb_hid_configure(struct usb_interface *intf) if (!usbhid->urbin) { err_hid("couldn't find an input interrupt endpoint"); + ret = -ENODEV; goto fail; } @@ -879,44 +870,25 @@ static struct hid_device *usb_hid_configure(struct usb_interface *intf) spin_lock_init(&usbhid->outlock); spin_lock_init(&usbhid->ctrllock); - hid->version = le16_to_cpu(hdesc->bcdHID); - hid->country = hdesc->bCountryCode; - hid->dev = &intf->dev; usbhid->intf = intf; usbhid->ifnum = interface->desc.bInterfaceNumber; - hid->bus = BUS_USB; - hid->vendor = le16_to_cpu(dev->descriptor.idVendor); - hid->product = le16_to_cpu(dev->descriptor.idProduct); - - usb_make_path(dev, hid->phys, sizeof(hid->phys)); - strlcat(hid->phys, "/input", sizeof(hid->phys)); - len = strlen(hid->phys); - if (len < sizeof(hid->phys) - 1) - snprintf(hid->phys + len, sizeof(hid->phys) - len, - "%d", intf->altsetting[0].desc.bInterfaceNumber); - - if (usb_string(dev, dev->descriptor.iSerialNumber, hid->uniq, 64) <= 0) - hid->uniq[0] = 0; - usbhid->urbctrl = usb_alloc_urb(0, GFP_KERNEL); - if (!usbhid->urbctrl) + if (!usbhid->urbctrl) { + ret = -ENOMEM; goto fail; + } usb_fill_control_urb(usbhid->urbctrl, dev, 0, (void *) usbhid->cr, usbhid->ctrlbuf, 1, hid_ctrl, hid); usbhid->urbctrl->setup_dma = usbhid->cr_dma; usbhid->urbctrl->transfer_dma = usbhid->ctrlbuf_dma; usbhid->urbctrl->transfer_flags |= (URB_NO_TRANSFER_DMA_MAP | URB_NO_SETUP_DMA_MAP); - hid->hidinput_input_event = usb_hidinput_input_event; - hid->hid_open = usbhid_open; - hid->hid_close = usbhid_close; -#ifdef CONFIG_USB_HIDDEV - hid->hiddev_hid_event = hiddev_hid_event; - hid->hiddev_report_event = hiddev_report_event; -#endif - hid->hid_output_raw_report = usbhid_output_raw_report; - return hid; + + usbhid_init_reports(hid); + hid_dump_device(hid); + + return 0; fail: usb_free_urb(usbhid->urbin); @@ -924,24 +896,18 @@ fail: usb_free_urb(usbhid->urbctrl); hid_free_buffers(dev, hid); kfree(usbhid); -fail_no_usbhid: - hid_free_device(hid); - - return NULL; +err: + return ret; } -static void hid_disconnect(struct usb_interface *intf) +static void usbhid_stop(struct hid_device *hid) { - struct hid_device *hid = usb_get_intfdata (intf); - struct usbhid_device *usbhid; + struct usbhid_device *usbhid = hid->driver_data; - if (!hid) + if (WARN_ON(!usbhid)) return; - usbhid = hid->driver_data; - spin_lock_irq(&usbhid->inlock); /* Sync with error handler */ - usb_set_intfdata(intf, NULL); set_bit(HID_DISCONNECTED, &usbhid->iofl); spin_unlock_irq(&usbhid->inlock); usb_kill_urb(usbhid->urbin); @@ -958,86 +924,100 @@ static void hid_disconnect(struct usb_interface *intf) if (hid->claimed & HID_CLAIMED_HIDRAW) hidraw_disconnect(hid); + hid->claimed = 0; + usb_free_urb(usbhid->urbin); usb_free_urb(usbhid->urbctrl); usb_free_urb(usbhid->urbout); hid_free_buffers(hid_to_usb_dev(hid), hid); kfree(usbhid); - hid_free_device(hid); + hid->driver_data = NULL; } +static struct hid_ll_driver usb_hid_driver = { + .parse = usbhid_parse, + .start = usbhid_start, + .stop = usbhid_stop, + .open = usbhid_open, + .close = usbhid_close, + .hidinput_input_event = usb_hidinput_input_event, +}; + static int hid_probe(struct usb_interface *intf, const struct usb_device_id *id) { + struct usb_device *dev = interface_to_usbdev(intf); struct hid_device *hid; - char path[64]; - int i; - char *c; + size_t len; + int ret; dbg_hid("HID probe called for ifnum %d\n", intf->altsetting->desc.bInterfaceNumber); - if (!(hid = usb_hid_configure(intf))) - return -ENODEV; - - usbhid_init_reports(hid); - hid_dump_device(hid); - if (hid->quirks & HID_QUIRK_RESET_LEDS) - usbhid_set_leds(hid); - - if (!hidinput_connect(hid)) - hid->claimed |= HID_CLAIMED_INPUT; - if (!hiddev_connect(hid)) - hid->claimed |= HID_CLAIMED_HIDDEV; - if (!hidraw_connect(hid)) - hid->claimed |= HID_CLAIMED_HIDRAW; + hid = hid_allocate_device(); + if (IS_ERR(hid)) + return PTR_ERR(hid); usb_set_intfdata(intf, hid); + hid->ll_driver = &usb_hid_driver; + hid->hid_output_raw_report = usbhid_output_raw_report; + hid->ff_init = hid_pidff_init; +#ifdef CONFIG_USB_HIDDEV + hid->hiddev_connect = hiddev_connect; + hid->hiddev_hid_event = hiddev_hid_event; + hid->hiddev_report_event = hiddev_report_event; +#endif + hid->dev.parent = &intf->dev; + hid->bus = BUS_USB; + hid->vendor = le16_to_cpu(dev->descriptor.idVendor); + hid->product = le16_to_cpu(dev->descriptor.idProduct); + hid->name[0] = 0; - if (!hid->claimed) { - printk ("HID device claimed by neither input, hiddev nor hidraw\n"); - hid_disconnect(intf); - return -ENODEV; + if (dev->manufacturer) + strlcpy(hid->name, dev->manufacturer, sizeof(hid->name)); + + if (dev->product) { + if (dev->manufacturer) + strlcat(hid->name, " ", sizeof(hid->name)); + strlcat(hid->name, dev->product, sizeof(hid->name)); } - if ((hid->claimed & HID_CLAIMED_INPUT)) - hid_ff_init(hid); + if (!strlen(hid->name)) + snprintf(hid->name, sizeof(hid->name), "HID %04x:%04x", + le16_to_cpu(dev->descriptor.idVendor), + le16_to_cpu(dev->descriptor.idProduct)); - if (hid->quirks & HID_QUIRK_SONY_PS3_CONTROLLER) - hid_fixup_sony_ps3_controller(interface_to_usbdev(intf), - intf->cur_altsetting->desc.bInterfaceNumber); + usb_make_path(dev, hid->phys, sizeof(hid->phys)); + strlcat(hid->phys, "/input", sizeof(hid->phys)); + len = strlen(hid->phys); + if (len < sizeof(hid->phys) - 1) + snprintf(hid->phys + len, sizeof(hid->phys) - len, + "%d", intf->altsetting[0].desc.bInterfaceNumber); - printk(KERN_INFO); + if (usb_string(dev, dev->descriptor.iSerialNumber, hid->uniq, 64) <= 0) + hid->uniq[0] = 0; - if (hid->claimed & HID_CLAIMED_INPUT) - printk("input"); - if ((hid->claimed & HID_CLAIMED_INPUT) && ((hid->claimed & HID_CLAIMED_HIDDEV) || - hid->claimed & HID_CLAIMED_HIDRAW)) - printk(","); - if (hid->claimed & HID_CLAIMED_HIDDEV) - printk("hiddev%d", hid->minor); - if ((hid->claimed & HID_CLAIMED_INPUT) && (hid->claimed & HID_CLAIMED_HIDDEV) && - (hid->claimed & HID_CLAIMED_HIDRAW)) - printk(","); - if (hid->claimed & HID_CLAIMED_HIDRAW) - printk("hidraw%d", ((struct hidraw*)hid->hidraw)->minor); - - c = "Device"; - for (i = 0; i < hid->maxcollection; i++) { - if (hid->collection[i].type == HID_COLLECTION_APPLICATION && - (hid->collection[i].usage & HID_USAGE_PAGE) == HID_UP_GENDESK && - (hid->collection[i].usage & 0xffff) < ARRAY_SIZE(hid_types)) { - c = hid_types[hid->collection[i].usage & 0xffff]; - break; - } + ret = hid_add_device(hid); + if (ret) { + if (ret != -ENODEV) + dev_err(&intf->dev, "can't add hid device: %d\n", ret); + goto err; } - usb_make_path(interface_to_usbdev(intf), path, 63); + return 0; +err: + hid_destroy_device(hid); + return ret; +} - printk(": USB HID v%x.%02x %s [%s] on %s\n", - hid->version >> 8, hid->version & 0xff, c, hid->name, path); +static void hid_disconnect(struct usb_interface *intf) +{ + struct hid_device *hid = usb_get_intfdata(intf); - return 0; + if (WARN_ON(!hid)) + return; + + hid_destroy_device(hid); } static int hid_suspend(struct usb_interface *intf, pm_message_t message) @@ -1107,9 +1087,22 @@ static struct usb_driver hid_driver = { .supports_autosuspend = 1, }; +static const struct hid_device_id hid_usb_table[] = { + { HID_USB_DEVICE(HID_ANY_ID, HID_ANY_ID) }, + { } +}; + +static struct hid_driver hid_usb_driver = { + .name = "generic-usb", + .id_table = hid_usb_table, +}; + static int __init hid_init(void) { int retval; + retval = hid_register_driver(&hid_usb_driver); + if (retval) + goto hid_register_fail; retval = usbhid_quirks_init(quirks_param); if (retval) goto usbhid_quirks_init_fail; @@ -1119,7 +1112,8 @@ static int __init hid_init(void) retval = usb_register(&hid_driver); if (retval) goto usb_register_fail; - info(DRIVER_VERSION ":" DRIVER_DESC); + printk(KERN_INFO KBUILD_MODNAME ": " DRIVER_VERSION ":" + DRIVER_DESC "\n"); return 0; usb_register_fail: @@ -1127,6 +1121,8 @@ usb_register_fail: hiddev_init_fail: usbhid_quirks_exit(); usbhid_quirks_init_fail: + hid_unregister_driver(&hid_usb_driver); +hid_register_fail: return retval; } @@ -1135,6 +1131,7 @@ static void __exit hid_exit(void) usb_deregister(&hid_driver); hiddev_exit(); usbhid_quirks_exit(); + hid_unregister_driver(&hid_usb_driver); } module_init(hid_init); diff --git a/drivers/hid/usbhid/hid-ff.c b/drivers/hid/usbhid/hid-ff.c deleted file mode 100644 index 1d0dac52f166..000000000000 --- a/drivers/hid/usbhid/hid-ff.c +++ /dev/null @@ -1,95 +0,0 @@ -/* - * Force feedback support for hid devices. - * Not all hid devices use the same protocol. For example, some use PID, - * other use their own proprietary procotol. - * - * Copyright (c) 2002-2004 Johann Deneux - */ - -/* - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - * - * Should you need to contact me, the author, you can do so by - * e-mail - mail your message to <johann.deneux@it.uu.se> - */ - -#include <linux/input.h> - -#undef DEBUG -#include <linux/usb.h> - -#include <linux/hid.h> -#include "usbhid.h" - -/* - * This table contains pointers to initializers. To add support for new - * devices, you need to add the USB vendor and product ids here. - */ -struct hid_ff_initializer { - u16 idVendor; - u16 idProduct; - int (*init)(struct hid_device*); -}; - -/* - * We try pidff when no other driver is found because PID is the - * standards compliant way of implementing force feedback in HID. - * pidff_init() will quickly abort if the device doesn't appear to - * be a PID device - */ -static struct hid_ff_initializer inits[] = { -#ifdef CONFIG_LOGITECH_FF - { 0x46d, 0xc211, hid_lgff_init }, /* Logitech Cordless rumble pad */ - { 0x46d, 0xc219, hid_lgff_init }, /* Logitech Cordless rumble pad 2 */ - { 0x46d, 0xc283, hid_lgff_init }, /* Logitech Wingman Force 3d */ - { 0x46d, 0xc286, hid_lgff_init }, /* Logitech Force 3D Pro Joystick */ - { 0x46d, 0xc294, hid_lgff_init }, /* Logitech Formula Force EX */ - { 0x46d, 0xc295, hid_lgff_init }, /* Logitech MOMO force wheel */ - { 0x46d, 0xca03, hid_lgff_init }, /* Logitech MOMO force wheel */ -#endif -#ifdef CONFIG_LOGIRUMBLEPAD2_FF - { 0x46d, 0xc218, hid_lg2ff_init }, /* Logitech Rumblepad 2 */ -#endif -#ifdef CONFIG_PANTHERLORD_FF - { 0x810, 0x0001, hid_plff_init }, /* "Twin USB Joystick" */ - { 0xe8f, 0x0003, hid_plff_init }, /* "GreenAsia Inc. USB Joystick " */ -#endif -#ifdef CONFIG_THRUSTMASTER_FF - { 0x44f, 0xb300, hid_tmff_init }, - { 0x44f, 0xb304, hid_tmff_init }, - { 0x44f, 0xb651, hid_tmff_init }, /* FGT Rumble Force Wheel */ - { 0x44f, 0xb654, hid_tmff_init }, /* FGT Force Feedback Wheel */ -#endif -#ifdef CONFIG_ZEROPLUS_FF - { 0xc12, 0x0005, hid_zpff_init }, - { 0xc12, 0x0030, hid_zpff_init }, -#endif - { 0, 0, hid_pidff_init} /* Matches anything */ -}; - -int hid_ff_init(struct hid_device* hid) -{ - struct hid_ff_initializer *init; - int vendor = le16_to_cpu(hid_to_usb_dev(hid)->descriptor.idVendor); - int product = le16_to_cpu(hid_to_usb_dev(hid)->descriptor.idProduct); - - for (init = inits; init->idVendor; init++) - if (init->idVendor == vendor && init->idProduct == product) - break; - - return init->init(hid); -} -EXPORT_SYMBOL_GPL(hid_ff_init); - diff --git a/drivers/hid/usbhid/hid-pidff.c b/drivers/hid/usbhid/hid-pidff.c index 011326178c06..484e3eec2f88 100644 --- a/drivers/hid/usbhid/hid-pidff.c +++ b/drivers/hid/usbhid/hid-pidff.c @@ -397,7 +397,6 @@ static void pidff_set_condition_report(struct pidff_device *pidff, effect->u.condition[i].left_saturation); pidff_set(&pidff->set_condition[PID_DEAD_BAND], effect->u.condition[i].deadband); - usbhid_wait_io(pidff->hid); usbhid_submit_report(pidff->hid, pidff->reports[PID_SET_CONDITION], USB_DIR_OUT); } @@ -512,7 +511,6 @@ static void pidff_playback_pid(struct pidff_device *pidff, int pid_id, int n) pidff->effect_operation[PID_LOOP_COUNT].value[0] = n; } - usbhid_wait_io(pidff->hid); usbhid_submit_report(pidff->hid, pidff->reports[PID_EFFECT_OPERATION], USB_DIR_OUT); } @@ -548,6 +546,9 @@ static int pidff_erase_effect(struct input_dev *dev, int effect_id) int pid_id = pidff->pid_id[effect_id]; debug("starting to erase %d/%d", effect_id, pidff->pid_id[effect_id]); + /* Wait for the queue to clear. We do not want a full fifo to + prevent the effect removal. */ + usbhid_wait_io(pidff->hid); pidff_playback_pid(pidff, pid_id, 0); pidff_erase_pid(pidff, pid_id); diff --git a/drivers/hid/usbhid/hid-quirks.c b/drivers/hid/usbhid/hid-quirks.c index b15f88249639..47ebe045f9b5 100644 --- a/drivers/hid/usbhid/hid-quirks.c +++ b/drivers/hid/usbhid/hid-quirks.c @@ -17,412 +17,7 @@ #include <linux/hid.h> -#define USB_VENDOR_ID_A4TECH 0x09da -#define USB_DEVICE_ID_A4TECH_WCP32PU 0x0006 -#define USB_DEVICE_ID_A4TECH_X5_005D 0x000a - -#define USB_VENDOR_ID_AASHIMA 0x06d6 -#define USB_DEVICE_ID_AASHIMA_GAMEPAD 0x0025 -#define USB_DEVICE_ID_AASHIMA_PREDATOR 0x0026 - -#define USB_VENDOR_ID_ACECAD 0x0460 -#define USB_DEVICE_ID_ACECAD_FLAIR 0x0004 -#define USB_DEVICE_ID_ACECAD_302 0x0008 - -#define USB_VENDOR_ID_ADS_TECH 0x06e1 -#define USB_DEVICE_ID_ADS_TECH_RADIO_SI470X 0xa155 - -#define USB_VENDOR_ID_AFATECH 0x15a4 -#define USB_DEVICE_ID_AFATECH_AF9016 0x9016 - -#define USB_VENDOR_ID_AIPTEK 0x08ca -#define USB_DEVICE_ID_AIPTEK_01 0x0001 -#define USB_DEVICE_ID_AIPTEK_10 0x0010 -#define USB_DEVICE_ID_AIPTEK_20 0x0020 -#define USB_DEVICE_ID_AIPTEK_21 0x0021 -#define USB_DEVICE_ID_AIPTEK_22 0x0022 -#define USB_DEVICE_ID_AIPTEK_23 0x0023 -#define USB_DEVICE_ID_AIPTEK_24 0x0024 - -#define USB_VENDOR_ID_AIRCABLE 0x16CA -#define USB_DEVICE_ID_AIRCABLE1 0x1502 - -#define USB_VENDOR_ID_ALCOR 0x058f -#define USB_DEVICE_ID_ALCOR_USBRS232 0x9720 - -#define USB_VENDOR_ID_ALPS 0x0433 -#define USB_DEVICE_ID_IBM_GAMEPAD 0x1101 - -#define USB_VENDOR_ID_APPLE 0x05ac -#define USB_DEVICE_ID_APPLE_MIGHTYMOUSE 0x0304 -#define USB_DEVICE_ID_APPLE_FOUNTAIN_ANSI 0x020e -#define USB_DEVICE_ID_APPLE_FOUNTAIN_ISO 0x020f -#define USB_DEVICE_ID_APPLE_GEYSER_ANSI 0x0214 -#define USB_DEVICE_ID_APPLE_GEYSER_ISO 0x0215 -#define USB_DEVICE_ID_APPLE_GEYSER_JIS 0x0216 -#define USB_DEVICE_ID_APPLE_GEYSER3_ANSI 0x0217 -#define USB_DEVICE_ID_APPLE_GEYSER3_ISO 0x0218 -#define USB_DEVICE_ID_APPLE_GEYSER3_JIS 0x0219 -#define USB_DEVICE_ID_APPLE_GEYSER4_ANSI 0x021a -#define USB_DEVICE_ID_APPLE_GEYSER4_ISO 0x021b -#define USB_DEVICE_ID_APPLE_GEYSER4_JIS 0x021c -#define USB_DEVICE_ID_APPLE_ALU_ANSI 0x0220 -#define USB_DEVICE_ID_APPLE_ALU_ISO 0x0221 -#define USB_DEVICE_ID_APPLE_ALU_JIS 0x0222 -#define USB_DEVICE_ID_APPLE_WELLSPRING_ANSI 0x0223 -#define USB_DEVICE_ID_APPLE_WELLSPRING_ISO 0x0224 -#define USB_DEVICE_ID_APPLE_WELLSPRING_JIS 0x0225 -#define USB_DEVICE_ID_APPLE_GEYSER4_HF_ANSI 0x0229 -#define USB_DEVICE_ID_APPLE_GEYSER4_HF_ISO 0x022a -#define USB_DEVICE_ID_APPLE_GEYSER4_HF_JIS 0x022b -#define USB_DEVICE_ID_APPLE_ALU_WIRELESS_ANSI 0x022c -#define USB_DEVICE_ID_APPLE_ALU_WIRELESS_ISO 0x022d -#define USB_DEVICE_ID_APPLE_ALU_WIRELESS_JIS 0x022e -#define USB_DEVICE_ID_APPLE_WELLSPRING2_ANSI 0x0230 -#define USB_DEVICE_ID_APPLE_WELLSPRING2_ISO 0x0231 -#define USB_DEVICE_ID_APPLE_WELLSPRING2_JIS 0x0232 -#define USB_DEVICE_ID_APPLE_FOUNTAIN_TP_ONLY 0x030a -#define USB_DEVICE_ID_APPLE_GEYSER1_TP_ONLY 0x030b -#define USB_DEVICE_ID_APPLE_IRCONTROL4 0x8242 - -#define USB_VENDOR_ID_ASUS 0x0b05 -#define USB_DEVICE_ID_ASUS_LCM 0x1726 - -#define USB_VENDOR_ID_ATEN 0x0557 -#define USB_DEVICE_ID_ATEN_UC100KM 0x2004 -#define USB_DEVICE_ID_ATEN_CS124U 0x2202 -#define USB_DEVICE_ID_ATEN_2PORTKVM 0x2204 -#define USB_DEVICE_ID_ATEN_4PORTKVM 0x2205 -#define USB_DEVICE_ID_ATEN_4PORTKVMC 0x2208 - -#define USB_VENDOR_ID_BELKIN 0x050d -#define USB_DEVICE_ID_FLIP_KVM 0x3201 - -#define USB_VENDOR_ID_BERKSHIRE 0x0c98 -#define USB_DEVICE_ID_BERKSHIRE_PCWD 0x1140 - -#define USB_VENDOR_ID_CHERRY 0x046a -#define USB_DEVICE_ID_CHERRY_CYMOTION 0x0023 - -#define USB_VENDOR_ID_CHIC 0x05fe -#define USB_DEVICE_ID_CHIC_GAMEPAD 0x0014 - -#define USB_VENDOR_ID_CIDC 0x1677 - -#define USB_VENDOR_ID_CMEDIA 0x0d8c -#define USB_DEVICE_ID_CM109 0x000e - -#define USB_VENDOR_ID_CODEMERCS 0x07c0 -#define USB_DEVICE_ID_CODEMERCS_IOW_FIRST 0x1500 -#define USB_DEVICE_ID_CODEMERCS_IOW_LAST 0x15ff - -#define USB_VENDOR_ID_CYGNAL 0x10c4 -#define USB_DEVICE_ID_CYGNAL_RADIO_SI470X 0x818a - -#define USB_VENDOR_ID_CYPRESS 0x04b4 -#define USB_DEVICE_ID_CYPRESS_MOUSE 0x0001 -#define USB_DEVICE_ID_CYPRESS_HIDCOM 0x5500 -#define USB_DEVICE_ID_CYPRESS_ULTRAMOUSE 0x7417 -#define USB_DEVICE_ID_CYPRESS_BARCODE_1 0xde61 -#define USB_DEVICE_ID_CYPRESS_BARCODE_2 0xde64 - -#define USB_VENDOR_ID_DELL 0x413c -#define USB_DEVICE_ID_DELL_W7658 0x2005 - -#define USB_VENDOR_ID_DELORME 0x1163 -#define USB_DEVICE_ID_DELORME_EARTHMATE 0x0100 -#define USB_DEVICE_ID_DELORME_EM_LT20 0x0200 - -#define USB_VENDOR_ID_DMI 0x0c0b -#define USB_DEVICE_ID_DMI_ENC 0x5fab - -#define USB_VENDOR_ID_ELO 0x04E7 -#define USB_DEVICE_ID_ELO_TS2700 0x0020 - -#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_GAMERON 0x0810 -#define USB_DEVICE_ID_GAMERON_DUAL_PSX_ADAPTOR 0x0001 - -#define USB_VENDOR_ID_GENERAL_TOUCH 0x0dfc - -#define USB_VENDOR_ID_GLAB 0x06c2 -#define USB_DEVICE_ID_4_PHIDGETSERVO_30 0x0038 -#define USB_DEVICE_ID_1_PHIDGETSERVO_30 0x0039 -#define USB_DEVICE_ID_0_0_4_IF_KIT 0x0040 -#define USB_DEVICE_ID_0_16_16_IF_KIT 0x0044 -#define USB_DEVICE_ID_8_8_8_IF_KIT 0x0045 -#define USB_DEVICE_ID_0_8_7_IF_KIT 0x0051 -#define USB_DEVICE_ID_0_8_8_IF_KIT 0x0053 -#define USB_DEVICE_ID_PHIDGET_MOTORCONTROL 0x0058 - -#define USB_VENDOR_ID_GOTOP 0x08f2 -#define USB_DEVICE_ID_SUPER_Q2 0x007f -#define USB_DEVICE_ID_GOGOPEN 0x00ce -#define USB_DEVICE_ID_PENPOWER 0x00f4 - -#define USB_VENDOR_ID_GRETAGMACBETH 0x0971 -#define USB_DEVICE_ID_GRETAGMACBETH_HUEY 0x2005 - -#define USB_VENDOR_ID_GRIFFIN 0x077d -#define USB_DEVICE_ID_POWERMATE 0x0410 -#define USB_DEVICE_ID_SOUNDKNOB 0x04AA - -#define USB_VENDOR_ID_GTCO 0x078c -#define USB_DEVICE_ID_GTCO_90 0x0090 -#define USB_DEVICE_ID_GTCO_100 0x0100 -#define USB_DEVICE_ID_GTCO_101 0x0101 -#define USB_DEVICE_ID_GTCO_103 0x0103 -#define USB_DEVICE_ID_GTCO_104 0x0104 -#define USB_DEVICE_ID_GTCO_105 0x0105 -#define USB_DEVICE_ID_GTCO_106 0x0106 -#define USB_DEVICE_ID_GTCO_107 0x0107 -#define USB_DEVICE_ID_GTCO_108 0x0108 -#define USB_DEVICE_ID_GTCO_200 0x0200 -#define USB_DEVICE_ID_GTCO_201 0x0201 -#define USB_DEVICE_ID_GTCO_202 0x0202 -#define USB_DEVICE_ID_GTCO_203 0x0203 -#define USB_DEVICE_ID_GTCO_204 0x0204 -#define USB_DEVICE_ID_GTCO_205 0x0205 -#define USB_DEVICE_ID_GTCO_206 0x0206 -#define USB_DEVICE_ID_GTCO_207 0x0207 -#define USB_DEVICE_ID_GTCO_300 0x0300 -#define USB_DEVICE_ID_GTCO_301 0x0301 -#define USB_DEVICE_ID_GTCO_302 0x0302 -#define USB_DEVICE_ID_GTCO_303 0x0303 -#define USB_DEVICE_ID_GTCO_304 0x0304 -#define USB_DEVICE_ID_GTCO_305 0x0305 -#define USB_DEVICE_ID_GTCO_306 0x0306 -#define USB_DEVICE_ID_GTCO_307 0x0307 -#define USB_DEVICE_ID_GTCO_308 0x0308 -#define USB_DEVICE_ID_GTCO_309 0x0309 -#define USB_DEVICE_ID_GTCO_400 0x0400 -#define USB_DEVICE_ID_GTCO_401 0x0401 -#define USB_DEVICE_ID_GTCO_402 0x0402 -#define USB_DEVICE_ID_GTCO_403 0x0403 -#define USB_DEVICE_ID_GTCO_404 0x0404 -#define USB_DEVICE_ID_GTCO_405 0x0405 -#define USB_DEVICE_ID_GTCO_500 0x0500 -#define USB_DEVICE_ID_GTCO_501 0x0501 -#define USB_DEVICE_ID_GTCO_502 0x0502 -#define USB_DEVICE_ID_GTCO_503 0x0503 -#define USB_DEVICE_ID_GTCO_504 0x0504 -#define USB_DEVICE_ID_GTCO_1000 0x1000 -#define USB_DEVICE_ID_GTCO_1001 0x1001 -#define USB_DEVICE_ID_GTCO_1002 0x1002 -#define USB_DEVICE_ID_GTCO_1003 0x1003 -#define USB_DEVICE_ID_GTCO_1004 0x1004 -#define USB_DEVICE_ID_GTCO_1005 0x1005 -#define USB_DEVICE_ID_GTCO_1006 0x1006 -#define USB_DEVICE_ID_GTCO_1007 0x1007 -#define USB_VENDOR_ID_HAPP 0x078b -#define USB_DEVICE_ID_UGCI_DRIVING 0x0010 -#define USB_DEVICE_ID_UGCI_FLYING 0x0020 -#define USB_DEVICE_ID_UGCI_FIGHTING 0x0030 - -#define USB_VENDOR_ID_IMATION 0x0718 -#define USB_DEVICE_ID_DISC_STAKKA 0xd000 - -#define USB_VENDOR_ID_KBGEAR 0x084e -#define USB_DEVICE_ID_KBGEAR_JAMSTUDIO 0x1001 - -#define USB_VENDOR_ID_LD 0x0f11 -#define USB_DEVICE_ID_LD_CASSY 0x1000 -#define USB_DEVICE_ID_LD_POCKETCASSY 0x1010 -#define USB_DEVICE_ID_LD_MOBILECASSY 0x1020 -#define USB_DEVICE_ID_LD_JWM 0x1080 -#define USB_DEVICE_ID_LD_DMMP 0x1081 -#define USB_DEVICE_ID_LD_UMIP 0x1090 -#define USB_DEVICE_ID_LD_XRAY1 0x1100 -#define USB_DEVICE_ID_LD_XRAY2 0x1101 -#define USB_DEVICE_ID_LD_VIDEOCOM 0x1200 -#define USB_DEVICE_ID_LD_COM3LAB 0x2000 -#define USB_DEVICE_ID_LD_TELEPORT 0x2010 -#define USB_DEVICE_ID_LD_NETWORKANALYSER 0x2020 -#define USB_DEVICE_ID_LD_POWERCONTROL 0x2030 -#define USB_DEVICE_ID_LD_MACHINETEST 0x2040 - -#define USB_VENDOR_ID_LOGITECH 0x046d -#define USB_DEVICE_ID_LOGITECH_LX3 0xc044 -#define USB_DEVICE_ID_LOGITECH_V150 0xc047 -#define USB_DEVICE_ID_LOGITECH_RECEIVER 0xc101 -#define USB_DEVICE_ID_LOGITECH_HARMONY 0xc110 -#define USB_DEVICE_ID_LOGITECH_HARMONY_2 0xc111 -#define USB_DEVICE_ID_LOGITECH_HARMONY_3 0xc112 -#define USB_DEVICE_ID_LOGITECH_HARMONY_4 0xc113 -#define USB_DEVICE_ID_LOGITECH_HARMONY_5 0xc114 -#define USB_DEVICE_ID_LOGITECH_HARMONY_6 0xc115 -#define USB_DEVICE_ID_LOGITECH_HARMONY_7 0xc116 -#define USB_DEVICE_ID_LOGITECH_HARMONY_8 0xc117 -#define USB_DEVICE_ID_LOGITECH_HARMONY_9 0xc118 -#define USB_DEVICE_ID_LOGITECH_HARMONY_10 0xc119 -#define USB_DEVICE_ID_LOGITECH_HARMONY_11 0xc11a -#define USB_DEVICE_ID_LOGITECH_HARMONY_12 0xc11b -#define USB_DEVICE_ID_LOGITECH_HARMONY_13 0xc11c -#define USB_DEVICE_ID_LOGITECH_HARMONY_14 0xc11d -#define USB_DEVICE_ID_LOGITECH_HARMONY_15 0xc11e -#define USB_DEVICE_ID_LOGITECH_HARMONY_16 0xc11f -#define USB_DEVICE_ID_LOGITECH_HARMONY_17 0xc120 -#define USB_DEVICE_ID_LOGITECH_HARMONY_18 0xc121 -#define USB_DEVICE_ID_LOGITECH_HARMONY_19 0xc122 -#define USB_DEVICE_ID_LOGITECH_HARMONY_20 0xc123 -#define USB_DEVICE_ID_LOGITECH_HARMONY_21 0xc124 -#define USB_DEVICE_ID_LOGITECH_HARMONY_22 0xc125 -#define USB_DEVICE_ID_LOGITECH_HARMONY_23 0xc126 -#define USB_DEVICE_ID_LOGITECH_HARMONY_24 0xc127 -#define USB_DEVICE_ID_LOGITECH_HARMONY_25 0xc128 -#define USB_DEVICE_ID_LOGITECH_HARMONY_26 0xc129 -#define USB_DEVICE_ID_LOGITECH_HARMONY_27 0xc12a -#define USB_DEVICE_ID_LOGITECH_HARMONY_28 0xc12b -#define USB_DEVICE_ID_LOGITECH_HARMONY_29 0xc12c -#define USB_DEVICE_ID_LOGITECH_HARMONY_30 0xc12d -#define USB_DEVICE_ID_LOGITECH_HARMONY_31 0xc12e -#define USB_DEVICE_ID_LOGITECH_HARMONY_32 0xc12f -#define USB_DEVICE_ID_LOGITECH_HARMONY_33 0xc130 -#define USB_DEVICE_ID_LOGITECH_HARMONY_34 0xc131 -#define USB_DEVICE_ID_LOGITECH_HARMONY_35 0xc132 -#define USB_DEVICE_ID_LOGITECH_HARMONY_36 0xc133 -#define USB_DEVICE_ID_LOGITECH_HARMONY_37 0xc134 -#define USB_DEVICE_ID_LOGITECH_HARMONY_38 0xc135 -#define USB_DEVICE_ID_LOGITECH_HARMONY_39 0xc136 -#define USB_DEVICE_ID_LOGITECH_HARMONY_40 0xc137 -#define USB_DEVICE_ID_LOGITECH_HARMONY_41 0xc138 -#define USB_DEVICE_ID_LOGITECH_HARMONY_42 0xc139 -#define USB_DEVICE_ID_LOGITECH_HARMONY_43 0xc13a -#define USB_DEVICE_ID_LOGITECH_HARMONY_44 0xc13b -#define USB_DEVICE_ID_LOGITECH_HARMONY_45 0xc13c -#define USB_DEVICE_ID_LOGITECH_HARMONY_46 0xc13d -#define USB_DEVICE_ID_LOGITECH_HARMONY_47 0xc13e -#define USB_DEVICE_ID_LOGITECH_HARMONY_48 0xc13f -#define USB_DEVICE_ID_LOGITECH_HARMONY_49 0xc140 -#define USB_DEVICE_ID_LOGITECH_HARMONY_50 0xc141 -#define USB_DEVICE_ID_LOGITECH_HARMONY_51 0xc142 -#define USB_DEVICE_ID_LOGITECH_HARMONY_52 0xc143 -#define USB_DEVICE_ID_LOGITECH_HARMONY_53 0xc144 -#define USB_DEVICE_ID_LOGITECH_HARMONY_54 0xc145 -#define USB_DEVICE_ID_LOGITECH_HARMONY_55 0xc146 -#define USB_DEVICE_ID_LOGITECH_HARMONY_56 0xc147 -#define USB_DEVICE_ID_LOGITECH_HARMONY_57 0xc148 -#define USB_DEVICE_ID_LOGITECH_HARMONY_58 0xc149 -#define USB_DEVICE_ID_LOGITECH_HARMONY_59 0xc14a -#define USB_DEVICE_ID_LOGITECH_HARMONY_60 0xc14b -#define USB_DEVICE_ID_LOGITECH_HARMONY_61 0xc14c -#define USB_DEVICE_ID_LOGITECH_HARMONY_62 0xc14d -#define USB_DEVICE_ID_LOGITECH_HARMONY_63 0xc14e -#define USB_DEVICE_ID_LOGITECH_HARMONY_64 0xc14f -#define USB_DEVICE_ID_LOGITECH_EXTREME_3D 0xc215 -#define USB_DEVICE_ID_LOGITECH_WHEEL 0xc294 -#define USB_DEVICE_ID_LOGITECH_ELITE_KBD 0xc30a -#define USB_DEVICE_ID_LOGITECH_KBD 0xc311 -#define USB_DEVICE_ID_S510_RECEIVER 0xc50c -#define USB_DEVICE_ID_S510_RECEIVER_2 0xc517 -#define USB_DEVICE_ID_LOGITECH_CORDLESS_DESKTOP_LX500 0xc512 -#define USB_DEVICE_ID_MX3000_RECEIVER 0xc513 -#define USB_DEVICE_ID_DINOVO_DESKTOP 0xc704 -#define USB_DEVICE_ID_DINOVO_EDGE 0xc714 -#define USB_DEVICE_ID_DINOVO_MINI 0xc71f - -#define USB_VENDOR_ID_MCC 0x09db -#define USB_DEVICE_ID_MCC_PMD1024LS 0x0076 -#define USB_DEVICE_ID_MCC_PMD1208LS 0x007a - -#define USB_VENDOR_ID_MGE 0x0463 -#define USB_DEVICE_ID_MGE_UPS 0xffff -#define USB_DEVICE_ID_MGE_UPS1 0x0001 - -#define USB_VENDOR_ID_MICROCHIP 0x04d8 -#define USB_DEVICE_ID_PICKIT1 0x0032 -#define USB_DEVICE_ID_PICKIT2 0x0033 - -#define USB_VENDOR_ID_MICROSOFT 0x045e -#define USB_DEVICE_ID_SIDEWINDER_GV 0x003b -#define USB_DEVICE_ID_WIRELESS_OPTICAL_DESKTOP_3_0 0x009d -#define USB_DEVICE_ID_DESKTOP_RECV_1028 0x00f9 -#define USB_DEVICE_ID_MS_NE4K 0x00db -#define USB_DEVICE_ID_MS_LK6K 0x00f9 - -#define USB_VENDOR_ID_MONTEREY 0x0566 -#define USB_DEVICE_ID_GENIUS_KB29E 0x3004 - -#define USB_VENDOR_ID_NCR 0x0404 -#define USB_DEVICE_ID_NCR_FIRST 0x0300 -#define USB_DEVICE_ID_NCR_LAST 0x03ff - -#define USB_VENDOR_ID_NATIONAL_SEMICONDUCTOR 0x0400 -#define USB_DEVICE_ID_N_S_HARMONY 0xc359 - -#define USB_VENDOR_ID_NATSU 0x08b7 -#define USB_DEVICE_ID_NATSU_GAMEPAD 0x0001 - -#define USB_VENDOR_ID_NEC 0x073e -#define USB_DEVICE_ID_NEC_USB_GAME_PAD 0x0301 - -#define USB_VENDOR_ID_ONTRAK 0x0a07 -#define USB_DEVICE_ID_ONTRAK_ADU100 0x0064 - -#define USB_VENDOR_ID_PANJIT 0x134c - -#define USB_VENDOR_ID_PANTHERLORD 0x0810 -#define USB_DEVICE_ID_PANTHERLORD_TWIN_USB_JOYSTICK 0x0001 - -#define USB_VENDOR_ID_PETALYNX 0x18b1 -#define USB_DEVICE_ID_PETALYNX_MAXTER_REMOTE 0x0037 - -#define USB_VENDOR_ID_PLAYDOTCOM 0x0b43 -#define USB_DEVICE_ID_PLAYDOTCOM_EMS_USBII 0x0003 - -#define USB_VENDOR_ID_SAITEK 0x06a3 -#define USB_DEVICE_ID_SAITEK_RUMBLEPAD 0xff17 - -#define USB_VENDOR_ID_SAMSUNG 0x0419 -#define USB_DEVICE_ID_SAMSUNG_IR_REMOTE 0x0001 - -#define USB_VENDOR_ID_SONY 0x054c -#define USB_DEVICE_ID_SONY_PS3_CONTROLLER 0x0268 - -#define USB_VENDOR_ID_SOUNDGRAPH 0x15c2 -#define USB_DEVICE_ID_SOUNDGRAPH_IMON_LCD 0x0038 - -#define USB_VENDOR_ID_SUN 0x0430 -#define USB_DEVICE_ID_RARITAN_KVM_DONGLE 0xcdab - -#define USB_VENDOR_ID_SUNPLUS 0x04fc -#define USB_DEVICE_ID_SUNPLUS_WDESKTOP 0x05d8 - -#define USB_VENDOR_ID_TOPMAX 0x0663 -#define USB_DEVICE_ID_TOPMAX_COBRAPAD 0x0103 - -#define USB_VENDOR_ID_TURBOX 0x062a -#define USB_DEVICE_ID_TURBOX_KEYBOARD 0x0201 - -#define USB_VENDOR_ID_VERNIER 0x08f7 -#define USB_DEVICE_ID_VERNIER_LABPRO 0x0001 -#define USB_DEVICE_ID_VERNIER_GOTEMP 0x0002 -#define USB_DEVICE_ID_VERNIER_SKIP 0x0003 -#define USB_DEVICE_ID_VERNIER_CYCLOPS 0x0004 -#define USB_DEVICE_ID_VERNIER_LCSPEC 0x0006 - -#define USB_VENDOR_ID_WACOM 0x056a - -#define USB_VENDOR_ID_WISEGROUP 0x0925 -#define USB_DEVICE_ID_1_PHIDGETSERVO_20 0x8101 -#define USB_DEVICE_ID_4_PHIDGETSERVO_20 0x8104 -#define USB_DEVICE_ID_8_8_4_IF_KIT 0x8201 -#define USB_DEVICE_ID_QUAD_USB_JOYPAD 0x8800 -#define USB_DEVICE_ID_DUAL_USB_JOYPAD 0x8866 - -#define USB_VENDOR_ID_WISEGROUP_LTD 0x6677 -#define USB_DEVICE_ID_SMARTJOY_DUAL_PLUS 0x8802 - -#define USB_VENDOR_ID_YEALINK 0x6993 -#define USB_DEVICE_ID_YEALINK_P1K_P4K_B2K 0xb001 - -#define USB_VENDOR_ID_KYE 0x0458 -#define USB_DEVICE_ID_KYE_GPEN_560 0x5003 +#include "../hid-ids.h" /* * Alphabetically sorted blacklist by quirk type. @@ -433,18 +28,10 @@ static const struct hid_blacklist { __u16 idProduct; __u32 quirks; } hid_blacklist[] = { - - { USB_VENDOR_ID_A4TECH, USB_DEVICE_ID_A4TECH_WCP32PU, HID_QUIRK_2WHEEL_MOUSE_HACK_7 }, - { USB_VENDOR_ID_A4TECH, USB_DEVICE_ID_A4TECH_X5_005D, HID_QUIRK_2WHEEL_MOUSE_HACK_B8 }, - { USB_VENDOR_ID_CYPRESS, USB_DEVICE_ID_CYPRESS_MOUSE, HID_QUIRK_2WHEEL_MOUSE_HACK_5 }, - - { USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_RECEIVER, HID_QUIRK_BAD_RELATIVE_KEYS }, - { USB_VENDOR_ID_AASHIMA, USB_DEVICE_ID_AASHIMA_GAMEPAD, HID_QUIRK_BADPAD }, { USB_VENDOR_ID_AASHIMA, USB_DEVICE_ID_AASHIMA_PREDATOR, HID_QUIRK_BADPAD }, { USB_VENDOR_ID_ALPS, USB_DEVICE_ID_IBM_GAMEPAD, HID_QUIRK_BADPAD }, { USB_VENDOR_ID_CHIC, USB_DEVICE_ID_CHIC_GAMEPAD, HID_QUIRK_BADPAD }, - { USB_VENDOR_ID_GAMERON, USB_DEVICE_ID_GAMERON_DUAL_PSX_ADAPTOR, HID_QUIRK_MULTI_INPUT }, { USB_VENDOR_ID_HAPP, USB_DEVICE_ID_UGCI_DRIVING, HID_QUIRK_BADPAD | HID_QUIRK_MULTI_INPUT }, { USB_VENDOR_ID_HAPP, USB_DEVICE_ID_UGCI_FLYING, HID_QUIRK_BADPAD | HID_QUIRK_MULTI_INPUT }, { USB_VENDOR_ID_HAPP, USB_DEVICE_ID_UGCI_FIGHTING, HID_QUIRK_BADPAD | HID_QUIRK_MULTI_INPUT }, @@ -453,169 +40,11 @@ static const struct hid_blacklist { { USB_VENDOR_ID_SAITEK, USB_DEVICE_ID_SAITEK_RUMBLEPAD, HID_QUIRK_BADPAD }, { USB_VENDOR_ID_TOPMAX, USB_DEVICE_ID_TOPMAX_COBRAPAD, HID_QUIRK_BADPAD }, - { USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_DINOVO_DESKTOP, HID_QUIRK_DUPLICATE_USAGES }, - { USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_DINOVO_EDGE, HID_QUIRK_DUPLICATE_USAGES }, - { USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_DINOVO_MINI, HID_QUIRK_DUPLICATE_USAGES }, - { USB_VENDOR_ID_AFATECH, USB_DEVICE_ID_AFATECH_AF9016, HID_QUIRK_FULLSPEED_INTERVAL }, - { USB_VENDOR_ID_BELKIN, USB_DEVICE_ID_FLIP_KVM, HID_QUIRK_HIDDEV }, - { USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_IRCONTROL4, HID_QUIRK_HIDDEV | HID_QUIRK_IGNORE_HIDINPUT }, - { USB_VENDOR_ID_SAMSUNG, USB_DEVICE_ID_SAMSUNG_IR_REMOTE, HID_QUIRK_HIDDEV | HID_QUIRK_IGNORE_HIDINPUT }, - { USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_SIDEWINDER_GV, HID_QUIRK_HIDINPUT }, - - { USB_VENDOR_ID_EZKEY, USB_DEVICE_ID_BTC_8193, HID_QUIRK_HWHEEL_WHEEL_INVERT }, - - { USB_VENDOR_ID_ADS_TECH, USB_DEVICE_ID_ADS_TECH_RADIO_SI470X, HID_QUIRK_IGNORE }, - { USB_VENDOR_ID_AIPTEK, USB_DEVICE_ID_AIPTEK_01, HID_QUIRK_IGNORE }, - { USB_VENDOR_ID_AIPTEK, USB_DEVICE_ID_AIPTEK_10, HID_QUIRK_IGNORE }, - { USB_VENDOR_ID_AIPTEK, USB_DEVICE_ID_AIPTEK_20, HID_QUIRK_IGNORE }, - { USB_VENDOR_ID_AIPTEK, USB_DEVICE_ID_AIPTEK_21, HID_QUIRK_IGNORE }, - { USB_VENDOR_ID_AIPTEK, USB_DEVICE_ID_AIPTEK_22, HID_QUIRK_IGNORE }, - { USB_VENDOR_ID_AIPTEK, USB_DEVICE_ID_AIPTEK_23, HID_QUIRK_IGNORE }, - { USB_VENDOR_ID_AIPTEK, USB_DEVICE_ID_AIPTEK_24, HID_QUIRK_IGNORE }, - { USB_VENDOR_ID_AIRCABLE, USB_DEVICE_ID_AIRCABLE1, HID_QUIRK_IGNORE }, - { USB_VENDOR_ID_ALCOR, USB_DEVICE_ID_ALCOR_USBRS232, HID_QUIRK_IGNORE }, - { USB_VENDOR_ID_ASUS, USB_DEVICE_ID_ASUS_LCM, HID_QUIRK_IGNORE}, - { USB_VENDOR_ID_BERKSHIRE, USB_DEVICE_ID_BERKSHIRE_PCWD, HID_QUIRK_IGNORE }, - { USB_VENDOR_ID_CIDC, 0x0103, HID_QUIRK_IGNORE }, - { USB_VENDOR_ID_CYGNAL, USB_DEVICE_ID_CYGNAL_RADIO_SI470X, HID_QUIRK_IGNORE }, - { USB_VENDOR_ID_CMEDIA, USB_DEVICE_ID_CM109, HID_QUIRK_IGNORE }, - { USB_VENDOR_ID_CYPRESS, USB_DEVICE_ID_CYPRESS_HIDCOM, HID_QUIRK_IGNORE }, - { USB_VENDOR_ID_CYPRESS, USB_DEVICE_ID_CYPRESS_ULTRAMOUSE, HID_QUIRK_IGNORE }, - { USB_VENDOR_ID_DELORME, USB_DEVICE_ID_DELORME_EARTHMATE, HID_QUIRK_IGNORE }, - { USB_VENDOR_ID_DELORME, USB_DEVICE_ID_DELORME_EM_LT20, HID_QUIRK_IGNORE }, - { USB_VENDOR_ID_ESSENTIAL_REALITY, USB_DEVICE_ID_ESSENTIAL_REALITY_P5, HID_QUIRK_IGNORE }, - { USB_VENDOR_ID_GENERAL_TOUCH, 0x0001, HID_QUIRK_IGNORE }, - { USB_VENDOR_ID_GENERAL_TOUCH, 0x0002, HID_QUIRK_IGNORE }, - { USB_VENDOR_ID_GENERAL_TOUCH, 0x0003, HID_QUIRK_IGNORE }, - { USB_VENDOR_ID_GENERAL_TOUCH, 0x0004, HID_QUIRK_IGNORE }, - { USB_VENDOR_ID_GLAB, USB_DEVICE_ID_4_PHIDGETSERVO_30, HID_QUIRK_IGNORE }, - { USB_VENDOR_ID_GLAB, USB_DEVICE_ID_1_PHIDGETSERVO_30, HID_QUIRK_IGNORE }, - { USB_VENDOR_ID_GLAB, USB_DEVICE_ID_0_0_4_IF_KIT, HID_QUIRK_IGNORE }, - { USB_VENDOR_ID_GLAB, USB_DEVICE_ID_0_16_16_IF_KIT, HID_QUIRK_IGNORE }, - { USB_VENDOR_ID_GLAB, USB_DEVICE_ID_8_8_8_IF_KIT, HID_QUIRK_IGNORE }, - { USB_VENDOR_ID_GLAB, USB_DEVICE_ID_0_8_7_IF_KIT, HID_QUIRK_IGNORE }, - { USB_VENDOR_ID_GLAB, USB_DEVICE_ID_0_8_8_IF_KIT, HID_QUIRK_IGNORE }, - { USB_VENDOR_ID_GLAB, USB_DEVICE_ID_PHIDGET_MOTORCONTROL, HID_QUIRK_IGNORE }, - { USB_VENDOR_ID_GOTOP, USB_DEVICE_ID_SUPER_Q2, HID_QUIRK_IGNORE }, - { USB_VENDOR_ID_GOTOP, USB_DEVICE_ID_GOGOPEN, HID_QUIRK_IGNORE }, - { USB_VENDOR_ID_GOTOP, USB_DEVICE_ID_PENPOWER, HID_QUIRK_IGNORE }, - { USB_VENDOR_ID_GRETAGMACBETH, USB_DEVICE_ID_GRETAGMACBETH_HUEY, HID_QUIRK_IGNORE }, - { USB_VENDOR_ID_GRIFFIN, USB_DEVICE_ID_POWERMATE, HID_QUIRK_IGNORE }, - { USB_VENDOR_ID_GRIFFIN, USB_DEVICE_ID_SOUNDKNOB, HID_QUIRK_IGNORE }, - { USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_90, HID_QUIRK_IGNORE }, - { USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_100, HID_QUIRK_IGNORE }, - { USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_101, HID_QUIRK_IGNORE }, - { USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_103, HID_QUIRK_IGNORE }, - { USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_104, HID_QUIRK_IGNORE }, - { USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_105, HID_QUIRK_IGNORE }, - { USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_106, HID_QUIRK_IGNORE }, - { USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_107, HID_QUIRK_IGNORE }, - { USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_108, HID_QUIRK_IGNORE }, - { USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_200, HID_QUIRK_IGNORE }, - { USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_201, HID_QUIRK_IGNORE }, - { USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_202, HID_QUIRK_IGNORE }, - { USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_203, HID_QUIRK_IGNORE }, - { USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_204, HID_QUIRK_IGNORE }, - { USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_205, HID_QUIRK_IGNORE }, - { USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_206, HID_QUIRK_IGNORE }, - { USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_207, HID_QUIRK_IGNORE }, - { USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_300, HID_QUIRK_IGNORE }, - { USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_301, HID_QUIRK_IGNORE }, - { USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_302, HID_QUIRK_IGNORE }, - { USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_303, HID_QUIRK_IGNORE }, - { USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_304, HID_QUIRK_IGNORE }, - { USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_305, HID_QUIRK_IGNORE }, - { USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_306, HID_QUIRK_IGNORE }, - { USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_307, HID_QUIRK_IGNORE }, - { USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_308, HID_QUIRK_IGNORE }, - { USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_309, HID_QUIRK_IGNORE }, - { USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_400, HID_QUIRK_IGNORE }, - { USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_401, HID_QUIRK_IGNORE }, - { USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_402, HID_QUIRK_IGNORE }, - { USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_403, HID_QUIRK_IGNORE }, - { USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_404, HID_QUIRK_IGNORE }, - { USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_405, HID_QUIRK_IGNORE }, - { USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_500, HID_QUIRK_IGNORE }, - { USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_501, HID_QUIRK_IGNORE }, - { USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_502, HID_QUIRK_IGNORE }, - { USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_503, HID_QUIRK_IGNORE }, - { USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_504, HID_QUIRK_IGNORE }, - { USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_1000, HID_QUIRK_IGNORE }, - { USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_1001, HID_QUIRK_IGNORE }, - { USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_1002, HID_QUIRK_IGNORE }, - { USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_1003, HID_QUIRK_IGNORE }, - { USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_1004, HID_QUIRK_IGNORE }, - { USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_1005, HID_QUIRK_IGNORE }, - { USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_1006, HID_QUIRK_IGNORE }, - { USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_1007, HID_QUIRK_IGNORE }, - { USB_VENDOR_ID_IMATION, USB_DEVICE_ID_DISC_STAKKA, HID_QUIRK_IGNORE }, - { USB_VENDOR_ID_KBGEAR, USB_DEVICE_ID_KBGEAR_JAMSTUDIO, HID_QUIRK_IGNORE }, - { USB_VENDOR_ID_LD, USB_DEVICE_ID_LD_CASSY, HID_QUIRK_IGNORE }, - { USB_VENDOR_ID_LD, USB_DEVICE_ID_LD_POCKETCASSY, HID_QUIRK_IGNORE }, - { USB_VENDOR_ID_LD, USB_DEVICE_ID_LD_MOBILECASSY, HID_QUIRK_IGNORE }, - { USB_VENDOR_ID_LD, USB_DEVICE_ID_LD_JWM, HID_QUIRK_IGNORE }, - { USB_VENDOR_ID_LD, USB_DEVICE_ID_LD_DMMP, HID_QUIRK_IGNORE }, - { USB_VENDOR_ID_LD, USB_DEVICE_ID_LD_UMIP, HID_QUIRK_IGNORE }, - { USB_VENDOR_ID_LD, USB_DEVICE_ID_LD_XRAY1, HID_QUIRK_IGNORE }, - { USB_VENDOR_ID_LD, USB_DEVICE_ID_LD_XRAY2, HID_QUIRK_IGNORE }, - { USB_VENDOR_ID_LD, USB_DEVICE_ID_LD_VIDEOCOM, HID_QUIRK_IGNORE }, - { USB_VENDOR_ID_LD, USB_DEVICE_ID_LD_COM3LAB, HID_QUIRK_IGNORE }, - { USB_VENDOR_ID_LD, USB_DEVICE_ID_LD_TELEPORT, HID_QUIRK_IGNORE }, - { USB_VENDOR_ID_LD, USB_DEVICE_ID_LD_NETWORKANALYSER, HID_QUIRK_IGNORE }, - { USB_VENDOR_ID_LD, USB_DEVICE_ID_LD_POWERCONTROL, HID_QUIRK_IGNORE }, - { USB_VENDOR_ID_LD, USB_DEVICE_ID_LD_MACHINETEST, HID_QUIRK_IGNORE }, - { USB_VENDOR_ID_MCC, USB_DEVICE_ID_MCC_PMD1024LS, HID_QUIRK_IGNORE }, - { USB_VENDOR_ID_MCC, USB_DEVICE_ID_MCC_PMD1208LS, HID_QUIRK_IGNORE }, - { USB_VENDOR_ID_MGE, USB_DEVICE_ID_MGE_UPS, HID_QUIRK_IGNORE }, - { USB_VENDOR_ID_MGE, USB_DEVICE_ID_MGE_UPS1, HID_QUIRK_IGNORE }, - { USB_VENDOR_ID_ONTRAK, USB_DEVICE_ID_ONTRAK_ADU100, HID_QUIRK_IGNORE }, - { USB_VENDOR_ID_ONTRAK, USB_DEVICE_ID_ONTRAK_ADU100 + 20, HID_QUIRK_IGNORE }, - { USB_VENDOR_ID_ONTRAK, USB_DEVICE_ID_ONTRAK_ADU100 + 30, HID_QUIRK_IGNORE }, - { USB_VENDOR_ID_ONTRAK, USB_DEVICE_ID_ONTRAK_ADU100 + 100, HID_QUIRK_IGNORE }, - { USB_VENDOR_ID_ONTRAK, USB_DEVICE_ID_ONTRAK_ADU100 + 108, HID_QUIRK_IGNORE }, - { USB_VENDOR_ID_ONTRAK, USB_DEVICE_ID_ONTRAK_ADU100 + 118, HID_QUIRK_IGNORE }, - { USB_VENDOR_ID_ONTRAK, USB_DEVICE_ID_ONTRAK_ADU100 + 200, HID_QUIRK_IGNORE }, - { USB_VENDOR_ID_ONTRAK, USB_DEVICE_ID_ONTRAK_ADU100 + 300, HID_QUIRK_IGNORE }, - { USB_VENDOR_ID_ONTRAK, USB_DEVICE_ID_ONTRAK_ADU100 + 400, HID_QUIRK_IGNORE }, - { USB_VENDOR_ID_ONTRAK, USB_DEVICE_ID_ONTRAK_ADU100 + 500, HID_QUIRK_IGNORE }, - { USB_VENDOR_ID_PANJIT, 0x0001, HID_QUIRK_IGNORE }, - { USB_VENDOR_ID_PANJIT, 0x0002, HID_QUIRK_IGNORE }, - { USB_VENDOR_ID_PANJIT, 0x0003, HID_QUIRK_IGNORE }, - { USB_VENDOR_ID_PANJIT, 0x0004, HID_QUIRK_IGNORE }, - { USB_VENDOR_ID_SOUNDGRAPH, USB_DEVICE_ID_SOUNDGRAPH_IMON_LCD, HID_QUIRK_IGNORE }, - { USB_VENDOR_ID_VERNIER, USB_DEVICE_ID_VERNIER_LABPRO, HID_QUIRK_IGNORE }, - { USB_VENDOR_ID_VERNIER, USB_DEVICE_ID_VERNIER_GOTEMP, HID_QUIRK_IGNORE }, - { USB_VENDOR_ID_VERNIER, USB_DEVICE_ID_VERNIER_SKIP, HID_QUIRK_IGNORE }, - { USB_VENDOR_ID_VERNIER, USB_DEVICE_ID_VERNIER_CYCLOPS, HID_QUIRK_IGNORE }, - { USB_VENDOR_ID_VERNIER, USB_DEVICE_ID_VERNIER_LCSPEC, HID_QUIRK_IGNORE }, - { USB_VENDOR_ID_WISEGROUP, USB_DEVICE_ID_4_PHIDGETSERVO_20, HID_QUIRK_IGNORE }, - { USB_VENDOR_ID_WISEGROUP, USB_DEVICE_ID_1_PHIDGETSERVO_20, HID_QUIRK_IGNORE }, - { USB_VENDOR_ID_WISEGROUP, USB_DEVICE_ID_8_8_4_IF_KIT, HID_QUIRK_IGNORE }, - { USB_VENDOR_ID_YEALINK, USB_DEVICE_ID_YEALINK_P1K_P4K_B2K, HID_QUIRK_IGNORE }, - - { USB_VENDOR_ID_ACECAD, USB_DEVICE_ID_ACECAD_FLAIR, HID_QUIRK_IGNORE }, - { USB_VENDOR_ID_ACECAD, USB_DEVICE_ID_ACECAD_302, HID_QUIRK_IGNORE }, - - { USB_VENDOR_ID_MICROCHIP, USB_DEVICE_ID_PICKIT1, HID_QUIRK_IGNORE }, - { USB_VENDOR_ID_MICROCHIP, USB_DEVICE_ID_PICKIT2, HID_QUIRK_IGNORE }, - - { USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_ELITE_KBD, HID_QUIRK_LOGITECH_IGNORE_DOUBLED_WHEEL | HID_QUIRK_LOGITECH_EXPANDED_KEYMAP }, - { USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_CORDLESS_DESKTOP_LX500, HID_QUIRK_LOGITECH_IGNORE_DOUBLED_WHEEL | HID_QUIRK_LOGITECH_EXPANDED_KEYMAP }, - { USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_LX3, HID_QUIRK_INVERT_HWHEEL }, - { USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_V150, HID_QUIRK_INVERT_HWHEEL }, - - { USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_MS_NE4K, HID_QUIRK_MICROSOFT_KEYS }, - { USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_MS_LK6K, HID_QUIRK_MICROSOFT_KEYS }, - - { USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_MIGHTYMOUSE, HID_QUIRK_MIGHTYMOUSE | HID_QUIRK_INVERT_HWHEEL }, - { USB_VENDOR_ID_PANTHERLORD, USB_DEVICE_ID_PANTHERLORD_TWIN_USB_JOYSTICK, HID_QUIRK_MULTI_INPUT | HID_QUIRK_SKIP_OUTPUT_REPORTS }, { USB_VENDOR_ID_PLAYDOTCOM, USB_DEVICE_ID_PLAYDOTCOM_EMS_USBII, HID_QUIRK_MULTI_INPUT }, - { USB_VENDOR_ID_SONY, USB_DEVICE_ID_SONY_PS3_CONTROLLER, HID_QUIRK_SONY_PS3_CONTROLLER | HID_QUIRK_HIDDEV }, - { USB_VENDOR_ID_ATEN, USB_DEVICE_ID_ATEN_UC100KM, HID_QUIRK_NOGET }, { USB_VENDOR_ID_ATEN, USB_DEVICE_ID_ATEN_CS124U, HID_QUIRK_NOGET }, { USB_VENDOR_ID_ATEN, USB_DEVICE_ID_ATEN_2PORTKVM, HID_QUIRK_NOGET }, @@ -623,144 +52,13 @@ static const struct hid_blacklist { { USB_VENDOR_ID_ATEN, USB_DEVICE_ID_ATEN_4PORTKVMC, 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_LOGITECH, USB_DEVICE_ID_LOGITECH_EXTREME_3D, HID_QUIRK_NOGET }, - { USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_WHEEL, HID_QUIRK_NOGET }, - { USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_WIRELESS_OPTICAL_DESKTOP_3_0, HID_QUIRK_NOGET }, - { USB_VENDOR_ID_PETALYNX, USB_DEVICE_ID_PETALYNX_MAXTER_REMOTE, HID_QUIRK_NOGET }, { USB_VENDOR_ID_SUN, USB_DEVICE_ID_RARITAN_KVM_DONGLE, HID_QUIRK_NOGET }, { USB_VENDOR_ID_TURBOX, USB_DEVICE_ID_TURBOX_KEYBOARD, HID_QUIRK_NOGET }, { USB_VENDOR_ID_WISEGROUP, USB_DEVICE_ID_DUAL_USB_JOYPAD, HID_QUIRK_NOGET | HID_QUIRK_MULTI_INPUT | HID_QUIRK_SKIP_OUTPUT_REPORTS }, { USB_VENDOR_ID_WISEGROUP, USB_DEVICE_ID_QUAD_USB_JOYPAD, HID_QUIRK_NOGET | HID_QUIRK_MULTI_INPUT }, { USB_VENDOR_ID_WISEGROUP_LTD, USB_DEVICE_ID_SMARTJOY_DUAL_PLUS, HID_QUIRK_NOGET | HID_QUIRK_MULTI_INPUT }, - - { USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_FOUNTAIN_ANSI, HID_QUIRK_APPLE_NUMLOCK_EMULATION | HID_QUIRK_APPLE_HAS_FN | HID_QUIRK_IGNORE_MOUSE }, - { USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_FOUNTAIN_ISO, HID_QUIRK_APPLE_NUMLOCK_EMULATION | HID_QUIRK_APPLE_HAS_FN | HID_QUIRK_IGNORE_MOUSE }, - { USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_GEYSER_ANSI, HID_QUIRK_APPLE_NUMLOCK_EMULATION | HID_QUIRK_APPLE_HAS_FN | HID_QUIRK_IGNORE_MOUSE }, - { USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_GEYSER_ISO, HID_QUIRK_APPLE_NUMLOCK_EMULATION | HID_QUIRK_APPLE_HAS_FN | HID_QUIRK_IGNORE_MOUSE | HID_QUIRK_APPLE_ISO_KEYBOARD}, - { USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_GEYSER_JIS, HID_QUIRK_APPLE_NUMLOCK_EMULATION | HID_QUIRK_APPLE_HAS_FN | HID_QUIRK_IGNORE_MOUSE }, - { USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_GEYSER3_ANSI, HID_QUIRK_APPLE_NUMLOCK_EMULATION | HID_QUIRK_APPLE_HAS_FN | HID_QUIRK_IGNORE_MOUSE }, - { USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_GEYSER3_ISO, HID_QUIRK_APPLE_NUMLOCK_EMULATION | HID_QUIRK_APPLE_HAS_FN | HID_QUIRK_IGNORE_MOUSE | HID_QUIRK_APPLE_ISO_KEYBOARD}, - { USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_GEYSER3_JIS, HID_QUIRK_APPLE_NUMLOCK_EMULATION | HID_QUIRK_APPLE_HAS_FN | HID_QUIRK_IGNORE_MOUSE }, - { USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_GEYSER4_ANSI, HID_QUIRK_APPLE_NUMLOCK_EMULATION | HID_QUIRK_APPLE_HAS_FN | HID_QUIRK_IGNORE_MOUSE }, - { USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_GEYSER4_ISO, HID_QUIRK_APPLE_NUMLOCK_EMULATION | HID_QUIRK_APPLE_HAS_FN | HID_QUIRK_IGNORE_MOUSE | HID_QUIRK_APPLE_ISO_KEYBOARD}, - { USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_GEYSER4_JIS, HID_QUIRK_APPLE_NUMLOCK_EMULATION | HID_QUIRK_APPLE_HAS_FN | HID_QUIRK_IGNORE_MOUSE }, - { USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_ALU_ANSI, HID_QUIRK_APPLE_HAS_FN }, - { USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_ALU_ISO, HID_QUIRK_APPLE_HAS_FN | HID_QUIRK_APPLE_ISO_KEYBOARD }, - { USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_ALU_JIS, HID_QUIRK_APPLE_HAS_FN }, - { USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_GEYSER4_HF_ANSI, HID_QUIRK_APPLE_NUMLOCK_EMULATION | HID_QUIRK_APPLE_HAS_FN | HID_QUIRK_IGNORE_MOUSE }, - { USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_GEYSER4_HF_ISO, HID_QUIRK_APPLE_NUMLOCK_EMULATION | HID_QUIRK_APPLE_HAS_FN | HID_QUIRK_IGNORE_MOUSE }, - { USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_GEYSER4_HF_JIS, HID_QUIRK_APPLE_NUMLOCK_EMULATION | HID_QUIRK_APPLE_HAS_FN | HID_QUIRK_IGNORE_MOUSE }, - { USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_ALU_WIRELESS_ANSI, HID_QUIRK_APPLE_NUMLOCK_EMULATION | HID_QUIRK_APPLE_HAS_FN }, - { USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_ALU_WIRELESS_ISO, HID_QUIRK_APPLE_NUMLOCK_EMULATION | HID_QUIRK_APPLE_HAS_FN | HID_QUIRK_APPLE_ISO_KEYBOARD }, - { USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_ALU_WIRELESS_JIS, HID_QUIRK_APPLE_NUMLOCK_EMULATION | HID_QUIRK_APPLE_HAS_FN }, - { USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_WELLSPRING_ANSI, HID_QUIRK_APPLE_HAS_FN | HID_QUIRK_IGNORE_MOUSE }, - { USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_WELLSPRING_ISO, HID_QUIRK_APPLE_HAS_FN | HID_QUIRK_APPLE_ISO_KEYBOARD | HID_QUIRK_IGNORE_MOUSE}, - { USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_WELLSPRING_JIS, HID_QUIRK_APPLE_HAS_FN | HID_QUIRK_IGNORE_MOUSE}, - { USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_WELLSPRING2_ANSI, HID_QUIRK_APPLE_HAS_FN | HID_QUIRK_IGNORE_MOUSE}, - { USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_WELLSPRING2_ISO, HID_QUIRK_APPLE_HAS_FN | HID_QUIRK_APPLE_ISO_KEYBOARD | HID_QUIRK_IGNORE_MOUSE }, - { USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_WELLSPRING2_JIS, HID_QUIRK_APPLE_HAS_FN | HID_QUIRK_IGNORE_MOUSE }, - { USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_FOUNTAIN_TP_ONLY, HID_QUIRK_APPLE_NUMLOCK_EMULATION | HID_QUIRK_APPLE_HAS_FN | HID_QUIRK_IGNORE_MOUSE }, - { USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_GEYSER1_TP_ONLY, HID_QUIRK_APPLE_NUMLOCK_EMULATION | HID_QUIRK_APPLE_HAS_FN | HID_QUIRK_IGNORE_MOUSE }, - - { USB_VENDOR_ID_DELL, USB_DEVICE_ID_DELL_W7658, HID_QUIRK_RESET_LEDS }, - { USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_KBD, HID_QUIRK_RESET_LEDS }, - { USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_HARMONY, HID_QUIRK_IGNORE }, - { USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_HARMONY_2, HID_QUIRK_IGNORE }, - { USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_HARMONY_3, HID_QUIRK_IGNORE }, - { USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_HARMONY_4, HID_QUIRK_IGNORE }, - { USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_HARMONY_5, HID_QUIRK_IGNORE }, - { USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_HARMONY_6, HID_QUIRK_IGNORE }, - { USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_HARMONY_7, HID_QUIRK_IGNORE }, - { USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_HARMONY_8, HID_QUIRK_IGNORE }, - { USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_HARMONY_9, HID_QUIRK_IGNORE }, - { USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_HARMONY_10, HID_QUIRK_IGNORE }, - { USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_HARMONY_11, HID_QUIRK_IGNORE }, - { USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_HARMONY_12, HID_QUIRK_IGNORE }, - { USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_HARMONY_13, HID_QUIRK_IGNORE }, - { USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_HARMONY_14, HID_QUIRK_IGNORE }, - { USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_HARMONY_15, HID_QUIRK_IGNORE }, - { USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_HARMONY_16, HID_QUIRK_IGNORE }, - { USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_HARMONY_17, HID_QUIRK_IGNORE }, - { USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_HARMONY_18, HID_QUIRK_IGNORE }, - { USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_HARMONY_19, HID_QUIRK_IGNORE }, - { USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_HARMONY_20, HID_QUIRK_IGNORE }, - { USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_HARMONY_21, HID_QUIRK_IGNORE }, - { USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_HARMONY_22, HID_QUIRK_IGNORE }, - { USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_HARMONY_23, HID_QUIRK_IGNORE }, - { USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_HARMONY_24, HID_QUIRK_IGNORE }, - { USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_HARMONY_25, HID_QUIRK_IGNORE }, - { USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_HARMONY_26, HID_QUIRK_IGNORE }, - { USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_HARMONY_27, HID_QUIRK_IGNORE }, - { USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_HARMONY_28, HID_QUIRK_IGNORE }, - { USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_HARMONY_29, HID_QUIRK_IGNORE }, - { USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_HARMONY_30, HID_QUIRK_IGNORE }, - { USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_HARMONY_31, HID_QUIRK_IGNORE }, - { USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_HARMONY_32, HID_QUIRK_IGNORE }, - { USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_HARMONY_33, HID_QUIRK_IGNORE }, - { USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_HARMONY_34, HID_QUIRK_IGNORE }, - { USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_HARMONY_35, HID_QUIRK_IGNORE }, - { USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_HARMONY_36, HID_QUIRK_IGNORE }, - { USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_HARMONY_37, HID_QUIRK_IGNORE }, - { USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_HARMONY_38, HID_QUIRK_IGNORE }, - { USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_HARMONY_39, HID_QUIRK_IGNORE }, - { USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_HARMONY_40, HID_QUIRK_IGNORE }, - { USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_HARMONY_41, HID_QUIRK_IGNORE }, - { USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_HARMONY_42, HID_QUIRK_IGNORE }, - { USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_HARMONY_43, HID_QUIRK_IGNORE }, - { USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_HARMONY_44, HID_QUIRK_IGNORE }, - { USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_HARMONY_45, HID_QUIRK_IGNORE }, - { USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_HARMONY_46, HID_QUIRK_IGNORE }, - { USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_HARMONY_47, HID_QUIRK_IGNORE }, - { USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_HARMONY_48, HID_QUIRK_IGNORE }, - { USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_HARMONY_49, HID_QUIRK_IGNORE }, - { USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_HARMONY_50, HID_QUIRK_IGNORE }, - { USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_HARMONY_51, HID_QUIRK_IGNORE }, - { USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_HARMONY_52, HID_QUIRK_IGNORE }, - { USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_HARMONY_53, HID_QUIRK_IGNORE }, - { USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_HARMONY_54, HID_QUIRK_IGNORE }, - { USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_HARMONY_55, HID_QUIRK_IGNORE }, - { USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_HARMONY_56, HID_QUIRK_IGNORE }, - { USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_HARMONY_57, HID_QUIRK_IGNORE }, - { USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_HARMONY_58, HID_QUIRK_IGNORE }, - { USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_HARMONY_59, HID_QUIRK_IGNORE }, - { USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_HARMONY_60, HID_QUIRK_IGNORE }, - { USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_HARMONY_61, HID_QUIRK_IGNORE }, - { USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_HARMONY_62, HID_QUIRK_IGNORE }, - { USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_HARMONY_63, HID_QUIRK_IGNORE }, - { USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_HARMONY_64, HID_QUIRK_IGNORE }, - { USB_VENDOR_ID_NATIONAL_SEMICONDUCTOR, USB_DEVICE_ID_N_S_HARMONY, HID_QUIRK_IGNORE }, - { USB_VENDOR_ID_KYE, USB_DEVICE_ID_KYE_GPEN_560, HID_QUIRK_IGNORE }, - - { 0, 0 } -}; - -/* Quirks for devices which require report descriptor fixup go here */ -static const struct hid_rdesc_blacklist { - __u16 idVendor; - __u16 idProduct; - __u32 quirks; -} hid_rdesc_blacklist[] = { - - { USB_VENDOR_ID_CHERRY, USB_DEVICE_ID_CHERRY_CYMOTION, HID_QUIRK_RDESC_CYMOTION }, - - { USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_MX3000_RECEIVER, HID_QUIRK_RDESC_LOGITECH }, - { USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_S510_RECEIVER, HID_QUIRK_RDESC_LOGITECH }, - { USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_S510_RECEIVER_2, HID_QUIRK_RDESC_LOGITECH }, - { USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_DESKTOP_RECV_1028, HID_QUIRK_RDESC_MICROSOFT_RECV_1028 }, - - { USB_VENDOR_ID_MONTEREY, USB_DEVICE_ID_GENIUS_KB29E, HID_QUIRK_RDESC_BUTTON_CONSUMER }, - - { USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_GEYSER4_JIS, HID_QUIRK_RDESC_MACBOOK_JIS }, - - { USB_VENDOR_ID_PETALYNX, USB_DEVICE_ID_PETALYNX_MAXTER_REMOTE, HID_QUIRK_RDESC_PETALYNX }, - - { USB_VENDOR_ID_SAMSUNG, USB_DEVICE_ID_SAMSUNG_IR_REMOTE, HID_QUIRK_RDESC_SAMSUNG_REMOTE }, - - { USB_VENDOR_ID_SUNPLUS, USB_DEVICE_ID_SUNPLUS_WDESKTOP, HID_QUIRK_RDESC_SUNPLUS_WDESKTOP }, - - { USB_VENDOR_ID_CYPRESS, USB_DEVICE_ID_CYPRESS_BARCODE_1, HID_QUIRK_RDESC_SWAPPED_MIN_MAX }, - { USB_VENDOR_ID_CYPRESS, USB_DEVICE_ID_CYPRESS_BARCODE_2, HID_QUIRK_RDESC_SWAPPED_MIN_MAX }, + { USB_VENDOR_ID_WISEGROUP_LTD2, USB_DEVICE_ID_SMARTJOY_DUAL_PLUS, HID_QUIRK_NOGET | HID_QUIRK_MULTI_INPUT }, { 0, 0 } }; @@ -974,16 +272,6 @@ u32 usbhid_lookup_quirk(const u16 idVendor, const u16 idProduct) u32 quirks = 0; const struct hid_blacklist *bl_entry = NULL; - /* Ignore all Wacom devices */ - if (idVendor == USB_VENDOR_ID_WACOM) - return HID_QUIRK_IGNORE; - - /* ignore all Code Mercenaries IOWarrior devices */ - if (idVendor == USB_VENDOR_ID_CODEMERCS) - if (idProduct >= USB_DEVICE_ID_CODEMERCS_IOW_FIRST && - idProduct <= USB_DEVICE_ID_CODEMERCS_IOW_LAST) - return HID_QUIRK_IGNORE; - /* NCR devices must not be queried for reports */ if (idVendor == USB_VENDOR_ID_NCR && idProduct >= USB_DEVICE_ID_NCR_FIRST && @@ -1002,221 +290,3 @@ u32 usbhid_lookup_quirk(const u16 idVendor, const u16 idProduct) } EXPORT_SYMBOL_GPL(usbhid_lookup_quirk); - -/* - * Cherry Cymotion keyboard have an invalid HID report descriptor, - * that needs fixing before we can parse it. - */ -static void usbhid_fixup_cymotion_descriptor(char *rdesc, int rsize) -{ - if (rsize >= 17 && rdesc[11] == 0x3c && rdesc[12] == 0x02) { - printk(KERN_INFO "Fixing up Cherry Cymotion report descriptor\n"); - rdesc[11] = rdesc[16] = 0xff; - rdesc[12] = rdesc[17] = 0x03; - } -} - - -/* - * Certain Logitech keyboards send in report #3 keys which are far - * above the logical maximum described in descriptor. This extends - * the original value of 0x28c of logical maximum to 0x104d - */ -static void usbhid_fixup_logitech_descriptor(unsigned char *rdesc, int rsize) -{ - if (rsize >= 90 && rdesc[83] == 0x26 - && rdesc[84] == 0x8c - && rdesc[85] == 0x02) { - printk(KERN_INFO "Fixing up Logitech keyboard report descriptor\n"); - rdesc[84] = rdesc[89] = 0x4d; - rdesc[85] = rdesc[90] = 0x10; - } -} - -static void usbhid_fixup_sunplus_wdesktop(unsigned char *rdesc, int rsize) -{ - if (rsize >= 107 && rdesc[104] == 0x26 - && rdesc[105] == 0x80 - && rdesc[106] == 0x03) { - printk(KERN_INFO "Fixing up Sunplus Wireless Desktop report descriptor\n"); - rdesc[105] = rdesc[110] = 0x03; - rdesc[106] = rdesc[111] = 0x21; - } -} - -/* - * Samsung IrDA remote controller (reports as Cypress USB Mouse). - * - * Vendor specific report #4 has a size of 48 bit, - * and therefore is not accepted when inspecting the descriptors. - * As a workaround we reinterpret the report as: - * Variable type, count 6, size 8 bit, log. maximum 255 - * The burden to reconstruct the data is moved into user space. - */ -static void usbhid_fixup_samsung_irda_descriptor(unsigned char *rdesc, - int rsize) -{ - if (rsize >= 182 && rdesc[175] == 0x25 - && rdesc[176] == 0x40 - && rdesc[177] == 0x75 - && rdesc[178] == 0x30 - && rdesc[179] == 0x95 - && rdesc[180] == 0x01 - && rdesc[182] == 0x40) { - printk(KERN_INFO "Fixing up Samsung IrDA report descriptor\n"); - rdesc[176] = 0xff; - rdesc[178] = 0x08; - rdesc[180] = 0x06; - rdesc[182] = 0x42; - } -} - -/* Petalynx Maxter Remote has maximum for consumer page set too low */ -static void usbhid_fixup_petalynx_descriptor(unsigned char *rdesc, int rsize) -{ - if (rsize >= 60 && rdesc[39] == 0x2a - && rdesc[40] == 0xf5 - && rdesc[41] == 0x00 - && rdesc[59] == 0x26 - && rdesc[60] == 0xf9 - && rdesc[61] == 0x00) { - printk(KERN_INFO "Fixing up Petalynx Maxter Remote report descriptor\n"); - rdesc[60] = 0xfa; - rdesc[40] = 0xfa; - } -} - -/* - * Some USB barcode readers from cypress have usage min and usage max in - * the wrong order - */ -static void usbhid_fixup_cypress_descriptor(unsigned char *rdesc, int rsize) -{ - short fixed = 0; - int i; - - for (i = 0; i < rsize - 4; i++) { - if (rdesc[i] == 0x29 && rdesc [i+2] == 0x19) { - unsigned char tmp; - - rdesc[i] = 0x19; rdesc[i+2] = 0x29; - tmp = rdesc[i+3]; - rdesc[i+3] = rdesc[i+1]; - rdesc[i+1] = tmp; - } - } - - if (fixed) - printk(KERN_INFO "Fixing up Cypress report descriptor\n"); -} - -/* - * MacBook JIS keyboard has wrong logical maximum - */ -static void usbhid_fixup_macbook_descriptor(unsigned char *rdesc, int rsize) -{ - if (rsize >= 60 && rdesc[53] == 0x65 - && rdesc[59] == 0x65) { - printk(KERN_INFO "Fixing up MacBook JIS keyboard report descriptor\n"); - rdesc[53] = rdesc[59] = 0xe7; - } -} - -static void usbhid_fixup_button_consumer_descriptor(unsigned char *rdesc, int rsize) -{ - if (rsize >= 30 && rdesc[29] == 0x05 - && rdesc[30] == 0x09) { - printk(KERN_INFO "Fixing up button/consumer in HID report descriptor\n"); - rdesc[30] = 0x0c; - } -} - -/* - * Microsoft Wireless Desktop Receiver (Model 1028) has several - * 'Usage Min/Max' where it ought to have 'Physical Min/Max' - */ -static void usbhid_fixup_microsoft_descriptor(unsigned char *rdesc, int rsize) -{ - if (rsize == 571 && rdesc[284] == 0x19 - && rdesc[286] == 0x2a - && rdesc[304] == 0x19 - && rdesc[306] == 0x29 - && rdesc[352] == 0x1a - && rdesc[355] == 0x2a - && rdesc[557] == 0x19 - && rdesc[559] == 0x29) { - printk(KERN_INFO "Fixing up Microsoft Wireless Receiver Model 1028 report descriptor\n"); - rdesc[284] = rdesc[304] = rdesc[557] = 0x35; - rdesc[352] = 0x36; - rdesc[286] = rdesc[355] = 0x46; - rdesc[306] = rdesc[559] = 0x45; - } -} - -static void __usbhid_fixup_report_descriptor(__u32 quirks, char *rdesc, unsigned rsize) -{ - if ((quirks & HID_QUIRK_RDESC_CYMOTION)) - usbhid_fixup_cymotion_descriptor(rdesc, rsize); - - if (quirks & HID_QUIRK_RDESC_LOGITECH) - usbhid_fixup_logitech_descriptor(rdesc, rsize); - - if (quirks & HID_QUIRK_RDESC_SWAPPED_MIN_MAX) - usbhid_fixup_cypress_descriptor(rdesc, rsize); - - if (quirks & HID_QUIRK_RDESC_PETALYNX) - usbhid_fixup_petalynx_descriptor(rdesc, rsize); - - if (quirks & HID_QUIRK_RDESC_MACBOOK_JIS) - usbhid_fixup_macbook_descriptor(rdesc, rsize); - - if (quirks & HID_QUIRK_RDESC_BUTTON_CONSUMER) - usbhid_fixup_button_consumer_descriptor(rdesc, rsize); - - if (quirks & HID_QUIRK_RDESC_SAMSUNG_REMOTE) - usbhid_fixup_samsung_irda_descriptor(rdesc, rsize); - - if (quirks & HID_QUIRK_RDESC_MICROSOFT_RECV_1028) - usbhid_fixup_microsoft_descriptor(rdesc, rsize); - - if (quirks & HID_QUIRK_RDESC_SUNPLUS_WDESKTOP) - usbhid_fixup_sunplus_wdesktop(rdesc, rsize); -} - -/** - * usbhid_fixup_report_descriptor: check if report descriptor needs fixup - * - * Description: - * Walks the hid_rdesc_blacklist[] array and checks whether the device - * is known to have broken report descriptor that needs to be fixed up - * prior to entering the HID parser - * - * Returns: nothing - */ -void usbhid_fixup_report_descriptor(const u16 idVendor, const u16 idProduct, - char *rdesc, unsigned rsize, char **quirks_param) -{ - int n, m; - u16 paramVendor, paramProduct; - u32 quirks; - - /* static rdesc quirk entries */ - for (n = 0; hid_rdesc_blacklist[n].idVendor; n++) - if (hid_rdesc_blacklist[n].idVendor == idVendor && - hid_rdesc_blacklist[n].idProduct == idProduct) - __usbhid_fixup_report_descriptor(hid_rdesc_blacklist[n].quirks, - rdesc, rsize); - - /* runtime rdesc quirk entries handling */ - for (n = 0; quirks_param[n] && n < MAX_USBHID_BOOT_QUIRKS; n++) { - m = sscanf(quirks_param[n], "0x%hx:0x%hx:0x%x", - ¶mVendor, ¶mProduct, &quirks); - - if (m != 3) - printk(KERN_WARNING - "Could not parse HID quirk module param %s\n", - quirks_param[n]); - else if (paramVendor == idVendor && paramProduct == idProduct) - __usbhid_fixup_report_descriptor(quirks, rdesc, rsize); - } -} diff --git a/drivers/hid/usbhid/hiddev.c b/drivers/hid/usbhid/hiddev.c index 842e9edb888e..babd65dd46ad 100644 --- a/drivers/hid/usbhid/hiddev.c +++ b/drivers/hid/usbhid/hiddev.c @@ -790,21 +790,23 @@ static struct usb_class_driver hiddev_class = { /* * This is where hid.c calls us to connect a hid device to the hiddev driver */ -int hiddev_connect(struct hid_device *hid) +int hiddev_connect(struct hid_device *hid, unsigned int force) { struct hiddev *hiddev; struct usbhid_device *usbhid = hid->driver_data; - int i; int retval; - for (i = 0; i < hid->maxcollection; i++) - if (hid->collection[i].type == - HID_COLLECTION_APPLICATION && - !IS_INPUT_APPLICATION(hid->collection[i].usage)) - break; + if (!force) { + unsigned int i; + for (i = 0; i < hid->maxcollection; i++) + if (hid->collection[i].type == + HID_COLLECTION_APPLICATION && + !IS_INPUT_APPLICATION(hid->collection[i].usage)) + break; - if (i == hid->maxcollection && (hid->quirks & HID_QUIRK_HIDDEV) == 0) - return -1; + if (i == hid->maxcollection) + return -1; + } if (!(hiddev = kzalloc(sizeof(struct hiddev), GFP_KERNEL))) return -1; diff --git a/drivers/hid/usbhid/usbhid.h b/drivers/hid/usbhid/usbhid.h index 62d2d7c925bd..abedb13c623e 100644 --- a/drivers/hid/usbhid/usbhid.h +++ b/drivers/hid/usbhid/usbhid.h @@ -67,7 +67,7 @@ struct usbhid_device { spinlock_t ctrllock; /* Control fifo spinlock */ struct urb *urbout; /* Output URB */ - struct hid_report *out[HID_CONTROL_FIFO_SIZE]; /* Output pipe fifo */ + struct hid_output_fifo out[HID_CONTROL_FIFO_SIZE]; /* Output pipe fifo */ unsigned char outhead, outtail; /* Output pipe fifo head & tail */ char *outbuf; /* Output buffer */ dma_addr_t outbuf_dma; /* Output buffer dma */ @@ -82,7 +82,7 @@ struct usbhid_device { }; #define hid_to_usb_dev(hid_dev) \ - container_of(hid_dev->dev->parent, struct usb_device, dev) + container_of(hid_dev->dev.parent->parent, struct usb_device, dev) #endif diff --git a/drivers/hid/usbhid/usbkbd.c b/drivers/hid/usbhid/usbkbd.c index 0caaafe01843..b342926dd7fc 100644 --- a/drivers/hid/usbhid/usbkbd.c +++ b/drivers/hid/usbhid/usbkbd.c @@ -105,14 +105,16 @@ static void usb_kbd_irq(struct urb *urb) if (usb_kbd_keycode[kbd->old[i]]) input_report_key(kbd->dev, usb_kbd_keycode[kbd->old[i]], 0); else - info("Unknown key (scancode %#x) released.", kbd->old[i]); + dev_info(&urb->dev->dev, + "Unknown key (scancode %#x) released.\n", kbd->old[i]); } if (kbd->new[i] > 3 && memscan(kbd->old + 2, kbd->new[i], 6) == kbd->old + 8) { if (usb_kbd_keycode[kbd->new[i]]) input_report_key(kbd->dev, usb_kbd_keycode[kbd->new[i]], 1); else - info("Unknown key (scancode %#x) pressed.", kbd->new[i]); + dev_info(&urb->dev->dev, + "Unknown key (scancode %#x) released.\n", kbd->new[i]); } } @@ -159,7 +161,8 @@ static void usb_kbd_led(struct urb *urb) struct usb_kbd *kbd = urb->context; if (urb->status) - warn("led urb status %d received", urb->status); + dev_warn(&urb->dev->dev, "led urb status %d received\n", + urb->status); if (*(kbd->leds) == kbd->newleds) return; @@ -352,7 +355,8 @@ static int __init usb_kbd_init(void) { int result = usb_register(&usb_kbd_driver); if (result == 0) - info(DRIVER_VERSION ":" DRIVER_DESC); + printk(KERN_INFO KBUILD_MODNAME ": " DRIVER_VERSION ":" + DRIVER_DESC "\n"); return result; } diff --git a/drivers/hid/usbhid/usbmouse.c b/drivers/hid/usbhid/usbmouse.c index 35689ef172cc..72ab4b268096 100644 --- a/drivers/hid/usbhid/usbmouse.c +++ b/drivers/hid/usbhid/usbmouse.c @@ -31,6 +31,11 @@ #include <linux/usb/input.h> #include <linux/hid.h> +/* for apple IDs */ +#ifdef CONFIG_USB_HID_MODULE +#include "../hid-ids.h" +#endif + /* * Version Information */ @@ -240,7 +245,8 @@ static int __init usb_mouse_init(void) { int retval = usb_register(&usb_mouse_driver); if (retval == 0) - info(DRIVER_VERSION ":" DRIVER_DESC); + printk(KERN_INFO KBUILD_MODNAME ": " DRIVER_VERSION ":" + DRIVER_DESC "\n"); return retval; } diff --git a/drivers/input/misc/hp_sdc_rtc.c b/drivers/input/misc/hp_sdc_rtc.c index daa9d4220331..82ec6b1b6467 100644 --- a/drivers/input/misc/hp_sdc_rtc.c +++ b/drivers/input/misc/hp_sdc_rtc.c @@ -458,35 +458,35 @@ static int hp_sdc_rtc_proc_output (char *buf) p += sprintf(p, "i8042 rtc\t: READ FAILED!\n"); } else { p += sprintf(p, "i8042 rtc\t: %ld.%02d seconds\n", - tv.tv_sec, tv.tv_usec/1000); + tv.tv_sec, (int)tv.tv_usec/1000); } if (hp_sdc_rtc_read_fhs(&tv)) { p += sprintf(p, "handshake\t: READ FAILED!\n"); } else { p += sprintf(p, "handshake\t: %ld.%02d seconds\n", - tv.tv_sec, tv.tv_usec/1000); + tv.tv_sec, (int)tv.tv_usec/1000); } if (hp_sdc_rtc_read_mt(&tv)) { p += sprintf(p, "alarm\t\t: READ FAILED!\n"); } else { p += sprintf(p, "alarm\t\t: %ld.%02d seconds\n", - tv.tv_sec, tv.tv_usec/1000); + tv.tv_sec, (int)tv.tv_usec/1000); } if (hp_sdc_rtc_read_dt(&tv)) { p += sprintf(p, "delay\t\t: READ FAILED!\n"); } else { p += sprintf(p, "delay\t\t: %ld.%02d seconds\n", - tv.tv_sec, tv.tv_usec/1000); + tv.tv_sec, (int)tv.tv_usec/1000); } if (hp_sdc_rtc_read_ct(&tv)) { p += sprintf(p, "periodic\t: READ FAILED!\n"); } else { p += sprintf(p, "periodic\t: %ld.%02d seconds\n", - tv.tv_sec, tv.tv_usec/1000); + tv.tv_sec, (int)tv.tv_usec/1000); } p += sprintf(p, diff --git a/drivers/input/serio/hp_sdc.c b/drivers/input/serio/hp_sdc.c index 0d395979b2d1..bfe49243f38b 100644 --- a/drivers/input/serio/hp_sdc.c +++ b/drivers/input/serio/hp_sdc.c @@ -323,7 +323,7 @@ static void hp_sdc_tasklet(unsigned long foo) * it back to the application. and be less verbose. */ printk(KERN_WARNING PREFIX "read timeout (%ius)!\n", - tv.tv_usec - hp_sdc.rtv.tv_usec); + (int)(tv.tv_usec - hp_sdc.rtv.tv_usec)); curr->idx += hp_sdc.rqty; hp_sdc.rqty = 0; tmp = curr->seq[curr->actidx]; diff --git a/drivers/isdn/mISDN/dsp_cmx.c b/drivers/isdn/mISDN/dsp_cmx.c index e92b1ba4b45e..c2f51cc50760 100644 --- a/drivers/isdn/mISDN/dsp_cmx.c +++ b/drivers/isdn/mISDN/dsp_cmx.c @@ -452,10 +452,10 @@ one_member: if (finddsp->features.pcm_id == dsp->features.pcm_id) { if (finddsp->pcm_slot_rx >= 0 && finddsp->pcm_slot_rx < sizeof(freeslots)) - freeslots[finddsp->pcm_slot_tx] = 0; + freeslots[finddsp->pcm_slot_rx] = 0; if (finddsp->pcm_slot_tx >= 0 && finddsp->pcm_slot_tx < sizeof(freeslots)) - freeslots[finddsp->pcm_slot_rx] = 0; + freeslots[finddsp->pcm_slot_tx] = 0; } } i = 0; diff --git a/drivers/isdn/mISDN/timerdev.c b/drivers/isdn/mISDN/timerdev.c index e7462924b505..875fabe16e36 100644 --- a/drivers/isdn/mISDN/timerdev.c +++ b/drivers/isdn/mISDN/timerdev.c @@ -61,7 +61,7 @@ mISDN_open(struct inode *ino, struct file *filep) init_waitqueue_head(&dev->wait); filep->private_data = dev; __module_get(THIS_MODULE); - return 0; + return nonseekable_open(ino, filep); } static int diff --git a/drivers/md/Kconfig b/drivers/md/Kconfig index 07d92c11b5d8..2281b5098e95 100644 --- a/drivers/md/Kconfig +++ b/drivers/md/Kconfig @@ -30,6 +30,20 @@ config BLK_DEV_MD If unsure, say N. +config MD_AUTODETECT + bool "Autodetect RAID arrays during kernel boot" + depends on BLK_DEV_MD=y + default y + ---help--- + If you say Y here, then the kernel will try to autodetect raid + arrays as part of its boot process. + + If you don't use raid and say Y, this autodetection can cause + a several-second delay in the boot time due to various + synchronisation steps that are part of this step. + + If unsure, say Y. + config MD_LINEAR tristate "Linear (append) mode" depends on BLK_DEV_MD diff --git a/drivers/mfd/Kconfig b/drivers/mfd/Kconfig index 0dae245c6259..5eff8ad834d6 100644 --- a/drivers/mfd/Kconfig +++ b/drivers/mfd/Kconfig @@ -87,6 +87,44 @@ config MFD_TC6393XB help Support for Toshiba Mobile IO Controller TC6393XB +config MFD_WM8400 + tristate "Support Wolfson Microelectronics WM8400" + help + Support for the Wolfson Microelecronics WM8400 PMIC and audio + CODEC. This driver adds provides common support for accessing + the device, additional drivers must be enabled in order to use + the functionality of the device. + +config MFD_WM8350 + tristate + +config MFD_WM8350_CONFIG_MODE_0 + bool + depends on MFD_WM8350 + +config MFD_WM8350_CONFIG_MODE_1 + bool + depends on MFD_WM8350 + +config MFD_WM8350_CONFIG_MODE_2 + bool + depends on MFD_WM8350 + +config MFD_WM8350_CONFIG_MODE_3 + bool + depends on MFD_WM8350 + +config MFD_WM8350_I2C + tristate "Support Wolfson Microelectronics WM8350 with I2C" + select MFD_WM8350 + depends on I2C + help + The WM8350 is an integrated audio and power management + subsystem with watchdog and RTC functionality for embedded + systems. This option enables core support for the WM8350 with + I2C as the control interface. Additional options must be + selected to enable support for the functionality of the chip. + endmenu menu "Multimedia Capabilities Port drivers" diff --git a/drivers/mfd/Makefile b/drivers/mfd/Makefile index 6abebe364419..759b1fe1c891 100644 --- a/drivers/mfd/Makefile +++ b/drivers/mfd/Makefile @@ -12,6 +12,11 @@ obj-$(CONFIG_MFD_T7L66XB) += t7l66xb.o obj-$(CONFIG_MFD_TC6387XB) += tc6387xb.o obj-$(CONFIG_MFD_TC6393XB) += tc6393xb.o +obj-$(CONFIG_MFD_WM8400) += wm8400-core.o +wm8350-objs := wm8350-core.o wm8350-regmap.o wm8350-gpio.o +obj-$(CONFIG_MFD_WM8350) += wm8350.o +obj-$(CONFIG_MFD_WM8350_I2C) += wm8350-i2c.o + obj-$(CONFIG_MFD_CORE) += mfd-core.o obj-$(CONFIG_MCP) += mcp-core.o diff --git a/drivers/mfd/wm8350-core.c b/drivers/mfd/wm8350-core.c new file mode 100644 index 000000000000..25a7a5d08bce --- /dev/null +++ b/drivers/mfd/wm8350-core.c @@ -0,0 +1,1273 @@ +/* + * wm8350-core.c -- Device access for Wolfson WM8350 + * + * Copyright 2007, 2008 Wolfson Microelectronics PLC. + * + * Author: Liam Girdwood, Mark Brown + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + */ + +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/init.h> +#include <linux/bug.h> +#include <linux/device.h> +#include <linux/delay.h> +#include <linux/interrupt.h> +#include <linux/workqueue.h> + +#include <linux/mfd/wm8350/core.h> +#include <linux/mfd/wm8350/audio.h> +#include <linux/mfd/wm8350/comparator.h> +#include <linux/mfd/wm8350/gpio.h> +#include <linux/mfd/wm8350/pmic.h> +#include <linux/mfd/wm8350/rtc.h> +#include <linux/mfd/wm8350/supply.h> +#include <linux/mfd/wm8350/wdt.h> + +#define WM8350_UNLOCK_KEY 0x0013 +#define WM8350_LOCK_KEY 0x0000 + +#define WM8350_CLOCK_CONTROL_1 0x28 +#define WM8350_AIF_TEST 0x74 + +/* debug */ +#define WM8350_BUS_DEBUG 0 +#if WM8350_BUS_DEBUG +#define dump(regs, src) do { \ + int i_; \ + u16 *src_ = src; \ + printk(KERN_DEBUG); \ + for (i_ = 0; i_ < regs; i_++) \ + printk(" 0x%4.4x", *src_++); \ + printk("\n"); \ +} while (0); +#else +#define dump(bytes, src) +#endif + +#define WM8350_LOCK_DEBUG 0 +#if WM8350_LOCK_DEBUG +#define ldbg(format, arg...) printk(format, ## arg) +#else +#define ldbg(format, arg...) +#endif + +/* + * WM8350 Device IO + */ +static DEFINE_MUTEX(io_mutex); +static DEFINE_MUTEX(reg_lock_mutex); +static DEFINE_MUTEX(auxadc_mutex); + +/* Perform a physical read from the device. + */ +static int wm8350_phys_read(struct wm8350 *wm8350, u8 reg, int num_regs, + u16 *dest) +{ + int i, ret; + int bytes = num_regs * 2; + + dev_dbg(wm8350->dev, "volatile read\n"); + ret = wm8350->read_dev(wm8350, reg, bytes, (char *)dest); + + for (i = reg; i < reg + num_regs; i++) { + /* Cache is CPU endian */ + dest[i - reg] = be16_to_cpu(dest[i - reg]); + + /* Satisfy non-volatile bits from cache */ + dest[i - reg] &= wm8350_reg_io_map[i].vol; + dest[i - reg] |= wm8350->reg_cache[i]; + + /* Mask out non-readable bits */ + dest[i - reg] &= wm8350_reg_io_map[i].readable; + } + + dump(num_regs, dest); + + return ret; +} + +static int wm8350_read(struct wm8350 *wm8350, u8 reg, int num_regs, u16 *dest) +{ + int i; + int end = reg + num_regs; + int ret = 0; + int bytes = num_regs * 2; + + if (wm8350->read_dev == NULL) + return -ENODEV; + + if ((reg + num_regs - 1) > WM8350_MAX_REGISTER) { + dev_err(wm8350->dev, "invalid reg %x\n", + reg + num_regs - 1); + return -EINVAL; + } + + dev_dbg(wm8350->dev, + "%s R%d(0x%2.2x) %d regs\n", __func__, reg, reg, num_regs); + +#if WM8350_BUS_DEBUG + /* we can _safely_ read any register, but warn if read not supported */ + for (i = reg; i < end; i++) { + if (!wm8350_reg_io_map[i].readable) + dev_warn(wm8350->dev, + "reg R%d is not readable\n", i); + } +#endif + + /* if any volatile registers are required, then read back all */ + for (i = reg; i < end; i++) + if (wm8350_reg_io_map[i].vol) + return wm8350_phys_read(wm8350, reg, num_regs, dest); + + /* no volatiles, then cache is good */ + dev_dbg(wm8350->dev, "cache read\n"); + memcpy(dest, &wm8350->reg_cache[reg], bytes); + dump(num_regs, dest); + return ret; +} + +static inline int is_reg_locked(struct wm8350 *wm8350, u8 reg) +{ + if (reg == WM8350_SECURITY || + wm8350->reg_cache[WM8350_SECURITY] == WM8350_UNLOCK_KEY) + return 0; + + if ((reg == WM8350_GPIO_CONFIGURATION_I_O) || + (reg >= WM8350_GPIO_FUNCTION_SELECT_1 && + reg <= WM8350_GPIO_FUNCTION_SELECT_4) || + (reg >= WM8350_BATTERY_CHARGER_CONTROL_1 && + reg <= WM8350_BATTERY_CHARGER_CONTROL_3)) + return 1; + return 0; +} + +static int wm8350_write(struct wm8350 *wm8350, u8 reg, int num_regs, u16 *src) +{ + int i; + int end = reg + num_regs; + int bytes = num_regs * 2; + + if (wm8350->write_dev == NULL) + return -ENODEV; + + if ((reg + num_regs - 1) > WM8350_MAX_REGISTER) { + dev_err(wm8350->dev, "invalid reg %x\n", + reg + num_regs - 1); + return -EINVAL; + } + + /* it's generally not a good idea to write to RO or locked registers */ + for (i = reg; i < end; i++) { + if (!wm8350_reg_io_map[i].writable) { + dev_err(wm8350->dev, + "attempted write to read only reg R%d\n", i); + return -EINVAL; + } + + if (is_reg_locked(wm8350, i)) { + dev_err(wm8350->dev, + "attempted write to locked reg R%d\n", i); + return -EINVAL; + } + + src[i - reg] &= wm8350_reg_io_map[i].writable; + + wm8350->reg_cache[i] = + (wm8350->reg_cache[i] & ~wm8350_reg_io_map[i].writable) + | src[i - reg]; + + src[i - reg] = cpu_to_be16(src[i - reg]); + } + + /* Actually write it out */ + return wm8350->write_dev(wm8350, reg, bytes, (char *)src); +} + +/* + * Safe read, modify, write methods + */ +int wm8350_clear_bits(struct wm8350 *wm8350, u16 reg, u16 mask) +{ + u16 data; + int err; + + mutex_lock(&io_mutex); + err = wm8350_read(wm8350, reg, 1, &data); + if (err) { + dev_err(wm8350->dev, "read from reg R%d failed\n", reg); + goto out; + } + + data &= ~mask; + err = wm8350_write(wm8350, reg, 1, &data); + if (err) + dev_err(wm8350->dev, "write to reg R%d failed\n", reg); +out: + mutex_unlock(&io_mutex); + return err; +} +EXPORT_SYMBOL_GPL(wm8350_clear_bits); + +int wm8350_set_bits(struct wm8350 *wm8350, u16 reg, u16 mask) +{ + u16 data; + int err; + + mutex_lock(&io_mutex); + err = wm8350_read(wm8350, reg, 1, &data); + if (err) { + dev_err(wm8350->dev, "read from reg R%d failed\n", reg); + goto out; + } + + data |= mask; + err = wm8350_write(wm8350, reg, 1, &data); + if (err) + dev_err(wm8350->dev, "write to reg R%d failed\n", reg); +out: + mutex_unlock(&io_mutex); + return err; +} +EXPORT_SYMBOL_GPL(wm8350_set_bits); + +u16 wm8350_reg_read(struct wm8350 *wm8350, int reg) +{ + u16 data; + int err; + + mutex_lock(&io_mutex); + err = wm8350_read(wm8350, reg, 1, &data); + if (err) + dev_err(wm8350->dev, "read from reg R%d failed\n", reg); + + mutex_unlock(&io_mutex); + return data; +} +EXPORT_SYMBOL_GPL(wm8350_reg_read); + +int wm8350_reg_write(struct wm8350 *wm8350, int reg, u16 val) +{ + int ret; + u16 data = val; + + mutex_lock(&io_mutex); + ret = wm8350_write(wm8350, reg, 1, &data); + if (ret) + dev_err(wm8350->dev, "write to reg R%d failed\n", reg); + mutex_unlock(&io_mutex); + return ret; +} +EXPORT_SYMBOL_GPL(wm8350_reg_write); + +int wm8350_block_read(struct wm8350 *wm8350, int start_reg, int regs, + u16 *dest) +{ + int err = 0; + + mutex_lock(&io_mutex); + err = wm8350_read(wm8350, start_reg, regs, dest); + if (err) + dev_err(wm8350->dev, "block read starting from R%d failed\n", + start_reg); + mutex_unlock(&io_mutex); + return err; +} +EXPORT_SYMBOL_GPL(wm8350_block_read); + +int wm8350_block_write(struct wm8350 *wm8350, int start_reg, int regs, + u16 *src) +{ + int ret = 0; + + mutex_lock(&io_mutex); + ret = wm8350_write(wm8350, start_reg, regs, src); + if (ret) + dev_err(wm8350->dev, "block write starting at R%d failed\n", + start_reg); + mutex_unlock(&io_mutex); + return ret; +} +EXPORT_SYMBOL_GPL(wm8350_block_write); + +int wm8350_reg_lock(struct wm8350 *wm8350) +{ + u16 key = WM8350_LOCK_KEY; + int ret; + + ldbg(__func__); + mutex_lock(&io_mutex); + ret = wm8350_write(wm8350, WM8350_SECURITY, 1, &key); + if (ret) + dev_err(wm8350->dev, "lock failed\n"); + mutex_unlock(&io_mutex); + return ret; +} +EXPORT_SYMBOL_GPL(wm8350_reg_lock); + +int wm8350_reg_unlock(struct wm8350 *wm8350) +{ + u16 key = WM8350_UNLOCK_KEY; + int ret; + + ldbg(__func__); + mutex_lock(&io_mutex); + ret = wm8350_write(wm8350, WM8350_SECURITY, 1, &key); + if (ret) + dev_err(wm8350->dev, "unlock failed\n"); + mutex_unlock(&io_mutex); + return ret; +} +EXPORT_SYMBOL_GPL(wm8350_reg_unlock); + +static void wm8350_irq_call_handler(struct wm8350 *wm8350, int irq) +{ + mutex_lock(&wm8350->irq_mutex); + + if (wm8350->irq[irq].handler) + wm8350->irq[irq].handler(wm8350, irq, wm8350->irq[irq].data); + else { + dev_err(wm8350->dev, "irq %d nobody cared. now masked.\n", + irq); + wm8350_mask_irq(wm8350, irq); + } + + mutex_unlock(&wm8350->irq_mutex); +} + +/* + * wm8350_irq_worker actually handles the interrupts. Since all + * interrupts are clear on read the IRQ line will be reasserted and + * the physical IRQ will be handled again if another interrupt is + * asserted while we run - in the normal course of events this is a + * rare occurrence so we save I2C/SPI reads. + */ +static void wm8350_irq_worker(struct work_struct *work) +{ + struct wm8350 *wm8350 = container_of(work, struct wm8350, irq_work); + u16 level_one, status1, status2, comp; + + /* TODO: Use block reads to improve performance? */ + level_one = wm8350_reg_read(wm8350, WM8350_SYSTEM_INTERRUPTS) + & ~wm8350_reg_read(wm8350, WM8350_SYSTEM_INTERRUPTS_MASK); + status1 = wm8350_reg_read(wm8350, WM8350_INT_STATUS_1) + & ~wm8350_reg_read(wm8350, WM8350_INT_STATUS_1_MASK); + status2 = wm8350_reg_read(wm8350, WM8350_INT_STATUS_2) + & ~wm8350_reg_read(wm8350, WM8350_INT_STATUS_2_MASK); + comp = wm8350_reg_read(wm8350, WM8350_COMPARATOR_INT_STATUS) + & ~wm8350_reg_read(wm8350, WM8350_COMPARATOR_INT_STATUS_MASK); + + /* over current */ + if (level_one & WM8350_OC_INT) { + u16 oc; + + oc = wm8350_reg_read(wm8350, WM8350_OVER_CURRENT_INT_STATUS); + oc &= ~wm8350_reg_read(wm8350, + WM8350_OVER_CURRENT_INT_STATUS_MASK); + + if (oc & WM8350_OC_LS_EINT) /* limit switch */ + wm8350_irq_call_handler(wm8350, WM8350_IRQ_OC_LS); + } + + /* under voltage */ + if (level_one & WM8350_UV_INT) { + u16 uv; + + uv = wm8350_reg_read(wm8350, WM8350_UNDER_VOLTAGE_INT_STATUS); + uv &= ~wm8350_reg_read(wm8350, + WM8350_UNDER_VOLTAGE_INT_STATUS_MASK); + + if (uv & WM8350_UV_DC1_EINT) + wm8350_irq_call_handler(wm8350, WM8350_IRQ_UV_DC1); + if (uv & WM8350_UV_DC2_EINT) + wm8350_irq_call_handler(wm8350, WM8350_IRQ_UV_DC2); + if (uv & WM8350_UV_DC3_EINT) + wm8350_irq_call_handler(wm8350, WM8350_IRQ_UV_DC3); + if (uv & WM8350_UV_DC4_EINT) + wm8350_irq_call_handler(wm8350, WM8350_IRQ_UV_DC4); + if (uv & WM8350_UV_DC5_EINT) + wm8350_irq_call_handler(wm8350, WM8350_IRQ_UV_DC5); + if (uv & WM8350_UV_DC6_EINT) + wm8350_irq_call_handler(wm8350, WM8350_IRQ_UV_DC6); + if (uv & WM8350_UV_LDO1_EINT) + wm8350_irq_call_handler(wm8350, WM8350_IRQ_UV_LDO1); + if (uv & WM8350_UV_LDO2_EINT) + wm8350_irq_call_handler(wm8350, WM8350_IRQ_UV_LDO2); + if (uv & WM8350_UV_LDO3_EINT) + wm8350_irq_call_handler(wm8350, WM8350_IRQ_UV_LDO3); + if (uv & WM8350_UV_LDO4_EINT) + wm8350_irq_call_handler(wm8350, WM8350_IRQ_UV_LDO4); + } + + /* charger, RTC */ + if (status1) { + if (status1 & WM8350_CHG_BAT_HOT_EINT) + wm8350_irq_call_handler(wm8350, + WM8350_IRQ_CHG_BAT_HOT); + if (status1 & WM8350_CHG_BAT_COLD_EINT) + wm8350_irq_call_handler(wm8350, + WM8350_IRQ_CHG_BAT_COLD); + if (status1 & WM8350_CHG_BAT_FAIL_EINT) + wm8350_irq_call_handler(wm8350, + WM8350_IRQ_CHG_BAT_FAIL); + if (status1 & WM8350_CHG_TO_EINT) + wm8350_irq_call_handler(wm8350, WM8350_IRQ_CHG_TO); + if (status1 & WM8350_CHG_END_EINT) + wm8350_irq_call_handler(wm8350, WM8350_IRQ_CHG_END); + if (status1 & WM8350_CHG_START_EINT) + wm8350_irq_call_handler(wm8350, WM8350_IRQ_CHG_START); + if (status1 & WM8350_CHG_FAST_RDY_EINT) + wm8350_irq_call_handler(wm8350, + WM8350_IRQ_CHG_FAST_RDY); + if (status1 & WM8350_CHG_VBATT_LT_3P9_EINT) + wm8350_irq_call_handler(wm8350, + WM8350_IRQ_CHG_VBATT_LT_3P9); + if (status1 & WM8350_CHG_VBATT_LT_3P1_EINT) + wm8350_irq_call_handler(wm8350, + WM8350_IRQ_CHG_VBATT_LT_3P1); + if (status1 & WM8350_CHG_VBATT_LT_2P85_EINT) + wm8350_irq_call_handler(wm8350, + WM8350_IRQ_CHG_VBATT_LT_2P85); + if (status1 & WM8350_RTC_ALM_EINT) + wm8350_irq_call_handler(wm8350, WM8350_IRQ_RTC_ALM); + if (status1 & WM8350_RTC_SEC_EINT) + wm8350_irq_call_handler(wm8350, WM8350_IRQ_RTC_SEC); + if (status1 & WM8350_RTC_PER_EINT) + wm8350_irq_call_handler(wm8350, WM8350_IRQ_RTC_PER); + } + + /* current sink, system, aux adc */ + if (status2) { + if (status2 & WM8350_CS1_EINT) + wm8350_irq_call_handler(wm8350, WM8350_IRQ_CS1); + if (status2 & WM8350_CS2_EINT) + wm8350_irq_call_handler(wm8350, WM8350_IRQ_CS2); + + if (status2 & WM8350_SYS_HYST_COMP_FAIL_EINT) + wm8350_irq_call_handler(wm8350, + WM8350_IRQ_SYS_HYST_COMP_FAIL); + if (status2 & WM8350_SYS_CHIP_GT115_EINT) + wm8350_irq_call_handler(wm8350, + WM8350_IRQ_SYS_CHIP_GT115); + if (status2 & WM8350_SYS_CHIP_GT140_EINT) + wm8350_irq_call_handler(wm8350, + WM8350_IRQ_SYS_CHIP_GT140); + if (status2 & WM8350_SYS_WDOG_TO_EINT) + wm8350_irq_call_handler(wm8350, + WM8350_IRQ_SYS_WDOG_TO); + + if (status2 & WM8350_AUXADC_DATARDY_EINT) + wm8350_irq_call_handler(wm8350, + WM8350_IRQ_AUXADC_DATARDY); + if (status2 & WM8350_AUXADC_DCOMP4_EINT) + wm8350_irq_call_handler(wm8350, + WM8350_IRQ_AUXADC_DCOMP4); + if (status2 & WM8350_AUXADC_DCOMP3_EINT) + wm8350_irq_call_handler(wm8350, + WM8350_IRQ_AUXADC_DCOMP3); + if (status2 & WM8350_AUXADC_DCOMP2_EINT) + wm8350_irq_call_handler(wm8350, + WM8350_IRQ_AUXADC_DCOMP2); + if (status2 & WM8350_AUXADC_DCOMP1_EINT) + wm8350_irq_call_handler(wm8350, + WM8350_IRQ_AUXADC_DCOMP1); + + if (status2 & WM8350_USB_LIMIT_EINT) + wm8350_irq_call_handler(wm8350, WM8350_IRQ_USB_LIMIT); + } + + /* wake, codec, ext */ + if (comp) { + if (comp & WM8350_WKUP_OFF_STATE_EINT) + wm8350_irq_call_handler(wm8350, + WM8350_IRQ_WKUP_OFF_STATE); + if (comp & WM8350_WKUP_HIB_STATE_EINT) + wm8350_irq_call_handler(wm8350, + WM8350_IRQ_WKUP_HIB_STATE); + if (comp & WM8350_WKUP_CONV_FAULT_EINT) + wm8350_irq_call_handler(wm8350, + WM8350_IRQ_WKUP_CONV_FAULT); + if (comp & WM8350_WKUP_WDOG_RST_EINT) + wm8350_irq_call_handler(wm8350, + WM8350_IRQ_WKUP_WDOG_RST); + if (comp & WM8350_WKUP_GP_PWR_ON_EINT) + wm8350_irq_call_handler(wm8350, + WM8350_IRQ_WKUP_GP_PWR_ON); + if (comp & WM8350_WKUP_ONKEY_EINT) + wm8350_irq_call_handler(wm8350, WM8350_IRQ_WKUP_ONKEY); + if (comp & WM8350_WKUP_GP_WAKEUP_EINT) + wm8350_irq_call_handler(wm8350, + WM8350_IRQ_WKUP_GP_WAKEUP); + + if (comp & WM8350_CODEC_JCK_DET_L_EINT) + wm8350_irq_call_handler(wm8350, + WM8350_IRQ_CODEC_JCK_DET_L); + if (comp & WM8350_CODEC_JCK_DET_R_EINT) + wm8350_irq_call_handler(wm8350, + WM8350_IRQ_CODEC_JCK_DET_R); + if (comp & WM8350_CODEC_MICSCD_EINT) + wm8350_irq_call_handler(wm8350, + WM8350_IRQ_CODEC_MICSCD); + if (comp & WM8350_CODEC_MICD_EINT) + wm8350_irq_call_handler(wm8350, WM8350_IRQ_CODEC_MICD); + + if (comp & WM8350_EXT_USB_FB_EINT) + wm8350_irq_call_handler(wm8350, WM8350_IRQ_EXT_USB_FB); + if (comp & WM8350_EXT_WALL_FB_EINT) + wm8350_irq_call_handler(wm8350, + WM8350_IRQ_EXT_WALL_FB); + if (comp & WM8350_EXT_BAT_FB_EINT) + wm8350_irq_call_handler(wm8350, WM8350_IRQ_EXT_BAT_FB); + } + + if (level_one & WM8350_GP_INT) { + int i; + u16 gpio; + + gpio = wm8350_reg_read(wm8350, WM8350_GPIO_INT_STATUS); + gpio &= ~wm8350_reg_read(wm8350, + WM8350_GPIO_INT_STATUS_MASK); + + for (i = 0; i < 12; i++) { + if (gpio & (1 << i)) + wm8350_irq_call_handler(wm8350, + WM8350_IRQ_GPIO(i)); + } + } + + enable_irq(wm8350->chip_irq); +} + +static irqreturn_t wm8350_irq(int irq, void *data) +{ + struct wm8350 *wm8350 = data; + + disable_irq_nosync(irq); + schedule_work(&wm8350->irq_work); + + return IRQ_HANDLED; +} + +int wm8350_register_irq(struct wm8350 *wm8350, int irq, + void (*handler) (struct wm8350 *, int, void *), + void *data) +{ + if (irq < 0 || irq > WM8350_NUM_IRQ || !handler) + return -EINVAL; + + if (wm8350->irq[irq].handler) + return -EBUSY; + + mutex_lock(&wm8350->irq_mutex); + wm8350->irq[irq].handler = handler; + wm8350->irq[irq].data = data; + mutex_unlock(&wm8350->irq_mutex); + + return 0; +} +EXPORT_SYMBOL_GPL(wm8350_register_irq); + +int wm8350_free_irq(struct wm8350 *wm8350, int irq) +{ + if (irq < 0 || irq > WM8350_NUM_IRQ) + return -EINVAL; + + mutex_lock(&wm8350->irq_mutex); + wm8350->irq[irq].handler = NULL; + mutex_unlock(&wm8350->irq_mutex); + return 0; +} +EXPORT_SYMBOL_GPL(wm8350_free_irq); + +int wm8350_mask_irq(struct wm8350 *wm8350, int irq) +{ + switch (irq) { + case WM8350_IRQ_CHG_BAT_HOT: + return wm8350_set_bits(wm8350, WM8350_INT_STATUS_1_MASK, + WM8350_IM_CHG_BAT_HOT_EINT); + case WM8350_IRQ_CHG_BAT_COLD: + return wm8350_set_bits(wm8350, WM8350_INT_STATUS_1_MASK, + WM8350_IM_CHG_BAT_COLD_EINT); + case WM8350_IRQ_CHG_BAT_FAIL: + return wm8350_set_bits(wm8350, WM8350_INT_STATUS_1_MASK, + WM8350_IM_CHG_BAT_FAIL_EINT); + case WM8350_IRQ_CHG_TO: + return wm8350_set_bits(wm8350, WM8350_INT_STATUS_1_MASK, + WM8350_IM_CHG_TO_EINT); + case WM8350_IRQ_CHG_END: + return wm8350_set_bits(wm8350, WM8350_INT_STATUS_1_MASK, + WM8350_IM_CHG_END_EINT); + case WM8350_IRQ_CHG_START: + return wm8350_set_bits(wm8350, WM8350_INT_STATUS_1_MASK, + WM8350_IM_CHG_START_EINT); + case WM8350_IRQ_CHG_FAST_RDY: + return wm8350_set_bits(wm8350, WM8350_INT_STATUS_1_MASK, + WM8350_IM_CHG_FAST_RDY_EINT); + case WM8350_IRQ_RTC_PER: + return wm8350_set_bits(wm8350, WM8350_INT_STATUS_1_MASK, + WM8350_IM_RTC_PER_EINT); + case WM8350_IRQ_RTC_SEC: + return wm8350_set_bits(wm8350, WM8350_INT_STATUS_1_MASK, + WM8350_IM_RTC_SEC_EINT); + case WM8350_IRQ_RTC_ALM: + return wm8350_set_bits(wm8350, WM8350_INT_STATUS_1_MASK, + WM8350_IM_RTC_ALM_EINT); + case WM8350_IRQ_CHG_VBATT_LT_3P9: + return wm8350_set_bits(wm8350, WM8350_INT_STATUS_1_MASK, + WM8350_IM_CHG_VBATT_LT_3P9_EINT); + case WM8350_IRQ_CHG_VBATT_LT_3P1: + return wm8350_set_bits(wm8350, WM8350_INT_STATUS_1_MASK, + WM8350_IM_CHG_VBATT_LT_3P1_EINT); + case WM8350_IRQ_CHG_VBATT_LT_2P85: + return wm8350_set_bits(wm8350, WM8350_INT_STATUS_1_MASK, + WM8350_IM_CHG_VBATT_LT_2P85_EINT); + case WM8350_IRQ_CS1: + return wm8350_set_bits(wm8350, WM8350_INT_STATUS_2_MASK, + WM8350_IM_CS1_EINT); + case WM8350_IRQ_CS2: + return wm8350_set_bits(wm8350, WM8350_INT_STATUS_2_MASK, + WM8350_IM_CS2_EINT); + case WM8350_IRQ_USB_LIMIT: + return wm8350_set_bits(wm8350, WM8350_INT_STATUS_2_MASK, + WM8350_IM_USB_LIMIT_EINT); + case WM8350_IRQ_AUXADC_DATARDY: + return wm8350_set_bits(wm8350, WM8350_INT_STATUS_2_MASK, + WM8350_IM_AUXADC_DATARDY_EINT); + case WM8350_IRQ_AUXADC_DCOMP4: + return wm8350_set_bits(wm8350, WM8350_INT_STATUS_2_MASK, + WM8350_IM_AUXADC_DCOMP4_EINT); + case WM8350_IRQ_AUXADC_DCOMP3: + return wm8350_set_bits(wm8350, WM8350_INT_STATUS_2_MASK, + WM8350_IM_AUXADC_DCOMP3_EINT); + case WM8350_IRQ_AUXADC_DCOMP2: + return wm8350_set_bits(wm8350, WM8350_INT_STATUS_2_MASK, + WM8350_IM_AUXADC_DCOMP2_EINT); + case WM8350_IRQ_AUXADC_DCOMP1: + return wm8350_set_bits(wm8350, WM8350_INT_STATUS_2_MASK, + WM8350_IM_AUXADC_DCOMP1_EINT); + case WM8350_IRQ_SYS_HYST_COMP_FAIL: + return wm8350_set_bits(wm8350, WM8350_INT_STATUS_2_MASK, + WM8350_IM_SYS_HYST_COMP_FAIL_EINT); + case WM8350_IRQ_SYS_CHIP_GT115: + return wm8350_set_bits(wm8350, WM8350_INT_STATUS_2_MASK, + WM8350_IM_SYS_CHIP_GT115_EINT); + case WM8350_IRQ_SYS_CHIP_GT140: + return wm8350_set_bits(wm8350, WM8350_INT_STATUS_2_MASK, + WM8350_IM_SYS_CHIP_GT140_EINT); + case WM8350_IRQ_SYS_WDOG_TO: + return wm8350_set_bits(wm8350, WM8350_INT_STATUS_2_MASK, + WM8350_IM_SYS_WDOG_TO_EINT); + case WM8350_IRQ_UV_LDO4: + return wm8350_set_bits(wm8350, + WM8350_UNDER_VOLTAGE_INT_STATUS_MASK, + WM8350_IM_UV_LDO4_EINT); + case WM8350_IRQ_UV_LDO3: + return wm8350_set_bits(wm8350, + WM8350_UNDER_VOLTAGE_INT_STATUS_MASK, + WM8350_IM_UV_LDO3_EINT); + case WM8350_IRQ_UV_LDO2: + return wm8350_set_bits(wm8350, + WM8350_UNDER_VOLTAGE_INT_STATUS_MASK, + WM8350_IM_UV_LDO2_EINT); + case WM8350_IRQ_UV_LDO1: + return wm8350_set_bits(wm8350, + WM8350_UNDER_VOLTAGE_INT_STATUS_MASK, + WM8350_IM_UV_LDO1_EINT); + case WM8350_IRQ_UV_DC6: + return wm8350_set_bits(wm8350, + WM8350_UNDER_VOLTAGE_INT_STATUS_MASK, + WM8350_IM_UV_DC6_EINT); + case WM8350_IRQ_UV_DC5: + return wm8350_set_bits(wm8350, + WM8350_UNDER_VOLTAGE_INT_STATUS_MASK, + WM8350_IM_UV_DC5_EINT); + case WM8350_IRQ_UV_DC4: + return wm8350_set_bits(wm8350, + WM8350_UNDER_VOLTAGE_INT_STATUS_MASK, + WM8350_IM_UV_DC4_EINT); + case WM8350_IRQ_UV_DC3: + return wm8350_set_bits(wm8350, + WM8350_UNDER_VOLTAGE_INT_STATUS_MASK, + WM8350_IM_UV_DC3_EINT); + case WM8350_IRQ_UV_DC2: + return wm8350_set_bits(wm8350, + WM8350_UNDER_VOLTAGE_INT_STATUS_MASK, + WM8350_IM_UV_DC2_EINT); + case WM8350_IRQ_UV_DC1: + return wm8350_set_bits(wm8350, + WM8350_UNDER_VOLTAGE_INT_STATUS_MASK, + WM8350_IM_UV_DC1_EINT); + case WM8350_IRQ_OC_LS: + return wm8350_set_bits(wm8350, + WM8350_OVER_CURRENT_INT_STATUS_MASK, + WM8350_IM_OC_LS_EINT); + case WM8350_IRQ_EXT_USB_FB: + return wm8350_set_bits(wm8350, + WM8350_COMPARATOR_INT_STATUS_MASK, + WM8350_IM_EXT_USB_FB_EINT); + case WM8350_IRQ_EXT_WALL_FB: + return wm8350_set_bits(wm8350, + WM8350_COMPARATOR_INT_STATUS_MASK, + WM8350_IM_EXT_WALL_FB_EINT); + case WM8350_IRQ_EXT_BAT_FB: + return wm8350_set_bits(wm8350, + WM8350_COMPARATOR_INT_STATUS_MASK, + WM8350_IM_EXT_BAT_FB_EINT); + case WM8350_IRQ_CODEC_JCK_DET_L: + return wm8350_set_bits(wm8350, + WM8350_COMPARATOR_INT_STATUS_MASK, + WM8350_IM_CODEC_JCK_DET_L_EINT); + case WM8350_IRQ_CODEC_JCK_DET_R: + return wm8350_set_bits(wm8350, + WM8350_COMPARATOR_INT_STATUS_MASK, + WM8350_IM_CODEC_JCK_DET_R_EINT); + case WM8350_IRQ_CODEC_MICSCD: + return wm8350_set_bits(wm8350, + WM8350_COMPARATOR_INT_STATUS_MASK, + WM8350_IM_CODEC_MICSCD_EINT); + case WM8350_IRQ_CODEC_MICD: + return wm8350_set_bits(wm8350, + WM8350_COMPARATOR_INT_STATUS_MASK, + WM8350_IM_CODEC_MICD_EINT); + case WM8350_IRQ_WKUP_OFF_STATE: + return wm8350_set_bits(wm8350, + WM8350_COMPARATOR_INT_STATUS_MASK, + WM8350_IM_WKUP_OFF_STATE_EINT); + case WM8350_IRQ_WKUP_HIB_STATE: + return wm8350_set_bits(wm8350, + WM8350_COMPARATOR_INT_STATUS_MASK, + WM8350_IM_WKUP_HIB_STATE_EINT); + case WM8350_IRQ_WKUP_CONV_FAULT: + return wm8350_set_bits(wm8350, + WM8350_COMPARATOR_INT_STATUS_MASK, + WM8350_IM_WKUP_CONV_FAULT_EINT); + case WM8350_IRQ_WKUP_WDOG_RST: + return wm8350_set_bits(wm8350, + WM8350_COMPARATOR_INT_STATUS_MASK, + WM8350_IM_WKUP_OFF_STATE_EINT); + case WM8350_IRQ_WKUP_GP_PWR_ON: + return wm8350_set_bits(wm8350, + WM8350_COMPARATOR_INT_STATUS_MASK, + WM8350_IM_WKUP_GP_PWR_ON_EINT); + case WM8350_IRQ_WKUP_ONKEY: + return wm8350_set_bits(wm8350, + WM8350_COMPARATOR_INT_STATUS_MASK, + WM8350_IM_WKUP_ONKEY_EINT); + case WM8350_IRQ_WKUP_GP_WAKEUP: + return wm8350_set_bits(wm8350, + WM8350_COMPARATOR_INT_STATUS_MASK, + WM8350_IM_WKUP_GP_WAKEUP_EINT); + case WM8350_IRQ_GPIO(0): + return wm8350_set_bits(wm8350, + WM8350_GPIO_INT_STATUS_MASK, + WM8350_IM_GP0_EINT); + case WM8350_IRQ_GPIO(1): + return wm8350_set_bits(wm8350, + WM8350_GPIO_INT_STATUS_MASK, + WM8350_IM_GP1_EINT); + case WM8350_IRQ_GPIO(2): + return wm8350_set_bits(wm8350, + WM8350_GPIO_INT_STATUS_MASK, + WM8350_IM_GP2_EINT); + case WM8350_IRQ_GPIO(3): + return wm8350_set_bits(wm8350, + WM8350_GPIO_INT_STATUS_MASK, + WM8350_IM_GP3_EINT); + case WM8350_IRQ_GPIO(4): + return wm8350_set_bits(wm8350, + WM8350_GPIO_INT_STATUS_MASK, + WM8350_IM_GP4_EINT); + case WM8350_IRQ_GPIO(5): + return wm8350_set_bits(wm8350, + WM8350_GPIO_INT_STATUS_MASK, + WM8350_IM_GP5_EINT); + case WM8350_IRQ_GPIO(6): + return wm8350_set_bits(wm8350, + WM8350_GPIO_INT_STATUS_MASK, + WM8350_IM_GP6_EINT); + case WM8350_IRQ_GPIO(7): + return wm8350_set_bits(wm8350, + WM8350_GPIO_INT_STATUS_MASK, + WM8350_IM_GP7_EINT); + case WM8350_IRQ_GPIO(8): + return wm8350_set_bits(wm8350, + WM8350_GPIO_INT_STATUS_MASK, + WM8350_IM_GP8_EINT); + case WM8350_IRQ_GPIO(9): + return wm8350_set_bits(wm8350, + WM8350_GPIO_INT_STATUS_MASK, + WM8350_IM_GP9_EINT); + case WM8350_IRQ_GPIO(10): + return wm8350_set_bits(wm8350, + WM8350_GPIO_INT_STATUS_MASK, + WM8350_IM_GP10_EINT); + case WM8350_IRQ_GPIO(11): + return wm8350_set_bits(wm8350, + WM8350_GPIO_INT_STATUS_MASK, + WM8350_IM_GP11_EINT); + case WM8350_IRQ_GPIO(12): + return wm8350_set_bits(wm8350, + WM8350_GPIO_INT_STATUS_MASK, + WM8350_IM_GP12_EINT); + default: + dev_warn(wm8350->dev, "Attempting to mask unknown IRQ %d\n", + irq); + return -EINVAL; + } + return 0; +} +EXPORT_SYMBOL_GPL(wm8350_mask_irq); + +int wm8350_unmask_irq(struct wm8350 *wm8350, int irq) +{ + switch (irq) { + case WM8350_IRQ_CHG_BAT_HOT: + return wm8350_clear_bits(wm8350, WM8350_INT_STATUS_1_MASK, + WM8350_IM_CHG_BAT_HOT_EINT); + case WM8350_IRQ_CHG_BAT_COLD: + return wm8350_clear_bits(wm8350, WM8350_INT_STATUS_1_MASK, + WM8350_IM_CHG_BAT_COLD_EINT); + case WM8350_IRQ_CHG_BAT_FAIL: + return wm8350_clear_bits(wm8350, WM8350_INT_STATUS_1_MASK, + WM8350_IM_CHG_BAT_FAIL_EINT); + case WM8350_IRQ_CHG_TO: + return wm8350_clear_bits(wm8350, WM8350_INT_STATUS_1_MASK, + WM8350_IM_CHG_TO_EINT); + case WM8350_IRQ_CHG_END: + return wm8350_clear_bits(wm8350, WM8350_INT_STATUS_1_MASK, + WM8350_IM_CHG_END_EINT); + case WM8350_IRQ_CHG_START: + return wm8350_clear_bits(wm8350, WM8350_INT_STATUS_1_MASK, + WM8350_IM_CHG_START_EINT); + case WM8350_IRQ_CHG_FAST_RDY: + return wm8350_clear_bits(wm8350, WM8350_INT_STATUS_1_MASK, + WM8350_IM_CHG_FAST_RDY_EINT); + case WM8350_IRQ_RTC_PER: + return wm8350_clear_bits(wm8350, WM8350_INT_STATUS_1_MASK, + WM8350_IM_RTC_PER_EINT); + case WM8350_IRQ_RTC_SEC: + return wm8350_clear_bits(wm8350, WM8350_INT_STATUS_1_MASK, + WM8350_IM_RTC_SEC_EINT); + case WM8350_IRQ_RTC_ALM: + return wm8350_clear_bits(wm8350, WM8350_INT_STATUS_1_MASK, + WM8350_IM_RTC_ALM_EINT); + case WM8350_IRQ_CHG_VBATT_LT_3P9: + return wm8350_clear_bits(wm8350, WM8350_INT_STATUS_1_MASK, + WM8350_IM_CHG_VBATT_LT_3P9_EINT); + case WM8350_IRQ_CHG_VBATT_LT_3P1: + return wm8350_clear_bits(wm8350, WM8350_INT_STATUS_1_MASK, + WM8350_IM_CHG_VBATT_LT_3P1_EINT); + case WM8350_IRQ_CHG_VBATT_LT_2P85: + return wm8350_clear_bits(wm8350, WM8350_INT_STATUS_1_MASK, + WM8350_IM_CHG_VBATT_LT_2P85_EINT); + case WM8350_IRQ_CS1: + return wm8350_clear_bits(wm8350, WM8350_INT_STATUS_2_MASK, + WM8350_IM_CS1_EINT); + case WM8350_IRQ_CS2: + return wm8350_clear_bits(wm8350, WM8350_INT_STATUS_2_MASK, + WM8350_IM_CS2_EINT); + case WM8350_IRQ_USB_LIMIT: + return wm8350_clear_bits(wm8350, WM8350_INT_STATUS_2_MASK, + WM8350_IM_USB_LIMIT_EINT); + case WM8350_IRQ_AUXADC_DATARDY: + return wm8350_clear_bits(wm8350, WM8350_INT_STATUS_2_MASK, + WM8350_IM_AUXADC_DATARDY_EINT); + case WM8350_IRQ_AUXADC_DCOMP4: + return wm8350_clear_bits(wm8350, WM8350_INT_STATUS_2_MASK, + WM8350_IM_AUXADC_DCOMP4_EINT); + case WM8350_IRQ_AUXADC_DCOMP3: + return wm8350_clear_bits(wm8350, WM8350_INT_STATUS_2_MASK, + WM8350_IM_AUXADC_DCOMP3_EINT); + case WM8350_IRQ_AUXADC_DCOMP2: + return wm8350_clear_bits(wm8350, WM8350_INT_STATUS_2_MASK, + WM8350_IM_AUXADC_DCOMP2_EINT); + case WM8350_IRQ_AUXADC_DCOMP1: + return wm8350_clear_bits(wm8350, WM8350_INT_STATUS_2_MASK, + WM8350_IM_AUXADC_DCOMP1_EINT); + case WM8350_IRQ_SYS_HYST_COMP_FAIL: + return wm8350_clear_bits(wm8350, WM8350_INT_STATUS_2_MASK, + WM8350_IM_SYS_HYST_COMP_FAIL_EINT); + case WM8350_IRQ_SYS_CHIP_GT115: + return wm8350_clear_bits(wm8350, WM8350_INT_STATUS_2_MASK, + WM8350_IM_SYS_CHIP_GT115_EINT); + case WM8350_IRQ_SYS_CHIP_GT140: + return wm8350_clear_bits(wm8350, WM8350_INT_STATUS_2_MASK, + WM8350_IM_SYS_CHIP_GT140_EINT); + case WM8350_IRQ_SYS_WDOG_TO: + return wm8350_clear_bits(wm8350, WM8350_INT_STATUS_2_MASK, + WM8350_IM_SYS_WDOG_TO_EINT); + case WM8350_IRQ_UV_LDO4: + return wm8350_clear_bits(wm8350, + WM8350_UNDER_VOLTAGE_INT_STATUS_MASK, + WM8350_IM_UV_LDO4_EINT); + case WM8350_IRQ_UV_LDO3: + return wm8350_clear_bits(wm8350, + WM8350_UNDER_VOLTAGE_INT_STATUS_MASK, + WM8350_IM_UV_LDO3_EINT); + case WM8350_IRQ_UV_LDO2: + return wm8350_clear_bits(wm8350, + WM8350_UNDER_VOLTAGE_INT_STATUS_MASK, + WM8350_IM_UV_LDO2_EINT); + case WM8350_IRQ_UV_LDO1: + return wm8350_clear_bits(wm8350, + WM8350_UNDER_VOLTAGE_INT_STATUS_MASK, + WM8350_IM_UV_LDO1_EINT); + case WM8350_IRQ_UV_DC6: + return wm8350_clear_bits(wm8350, + WM8350_UNDER_VOLTAGE_INT_STATUS_MASK, + WM8350_IM_UV_DC6_EINT); + case WM8350_IRQ_UV_DC5: + return wm8350_clear_bits(wm8350, + WM8350_UNDER_VOLTAGE_INT_STATUS_MASK, + WM8350_IM_UV_DC5_EINT); + case WM8350_IRQ_UV_DC4: + return wm8350_clear_bits(wm8350, + WM8350_UNDER_VOLTAGE_INT_STATUS_MASK, + WM8350_IM_UV_DC4_EINT); + case WM8350_IRQ_UV_DC3: + return wm8350_clear_bits(wm8350, + WM8350_UNDER_VOLTAGE_INT_STATUS_MASK, + WM8350_IM_UV_DC3_EINT); + case WM8350_IRQ_UV_DC2: + return wm8350_clear_bits(wm8350, + WM8350_UNDER_VOLTAGE_INT_STATUS_MASK, + WM8350_IM_UV_DC2_EINT); + case WM8350_IRQ_UV_DC1: + return wm8350_clear_bits(wm8350, + WM8350_UNDER_VOLTAGE_INT_STATUS_MASK, + WM8350_IM_UV_DC1_EINT); + case WM8350_IRQ_OC_LS: + return wm8350_clear_bits(wm8350, + WM8350_OVER_CURRENT_INT_STATUS_MASK, + WM8350_IM_OC_LS_EINT); + case WM8350_IRQ_EXT_USB_FB: + return wm8350_clear_bits(wm8350, + WM8350_COMPARATOR_INT_STATUS_MASK, + WM8350_IM_EXT_USB_FB_EINT); + case WM8350_IRQ_EXT_WALL_FB: + return wm8350_clear_bits(wm8350, + WM8350_COMPARATOR_INT_STATUS_MASK, + WM8350_IM_EXT_WALL_FB_EINT); + case WM8350_IRQ_EXT_BAT_FB: + return wm8350_clear_bits(wm8350, + WM8350_COMPARATOR_INT_STATUS_MASK, + WM8350_IM_EXT_BAT_FB_EINT); + case WM8350_IRQ_CODEC_JCK_DET_L: + return wm8350_clear_bits(wm8350, + WM8350_COMPARATOR_INT_STATUS_MASK, + WM8350_IM_CODEC_JCK_DET_L_EINT); + case WM8350_IRQ_CODEC_JCK_DET_R: + return wm8350_clear_bits(wm8350, + WM8350_COMPARATOR_INT_STATUS_MASK, + WM8350_IM_CODEC_JCK_DET_R_EINT); + case WM8350_IRQ_CODEC_MICSCD: + return wm8350_clear_bits(wm8350, + WM8350_COMPARATOR_INT_STATUS_MASK, + WM8350_IM_CODEC_MICSCD_EINT); + case WM8350_IRQ_CODEC_MICD: + return wm8350_clear_bits(wm8350, + WM8350_COMPARATOR_INT_STATUS_MASK, + WM8350_IM_CODEC_MICD_EINT); + case WM8350_IRQ_WKUP_OFF_STATE: + return wm8350_clear_bits(wm8350, + WM8350_COMPARATOR_INT_STATUS_MASK, + WM8350_IM_WKUP_OFF_STATE_EINT); + case WM8350_IRQ_WKUP_HIB_STATE: + return wm8350_clear_bits(wm8350, + WM8350_COMPARATOR_INT_STATUS_MASK, + WM8350_IM_WKUP_HIB_STATE_EINT); + case WM8350_IRQ_WKUP_CONV_FAULT: + return wm8350_clear_bits(wm8350, + WM8350_COMPARATOR_INT_STATUS_MASK, + WM8350_IM_WKUP_CONV_FAULT_EINT); + case WM8350_IRQ_WKUP_WDOG_RST: + return wm8350_clear_bits(wm8350, + WM8350_COMPARATOR_INT_STATUS_MASK, + WM8350_IM_WKUP_OFF_STATE_EINT); + case WM8350_IRQ_WKUP_GP_PWR_ON: + return wm8350_clear_bits(wm8350, + WM8350_COMPARATOR_INT_STATUS_MASK, + WM8350_IM_WKUP_GP_PWR_ON_EINT); + case WM8350_IRQ_WKUP_ONKEY: + return wm8350_clear_bits(wm8350, + WM8350_COMPARATOR_INT_STATUS_MASK, + WM8350_IM_WKUP_ONKEY_EINT); + case WM8350_IRQ_WKUP_GP_WAKEUP: + return wm8350_clear_bits(wm8350, + WM8350_COMPARATOR_INT_STATUS_MASK, + WM8350_IM_WKUP_GP_WAKEUP_EINT); + case WM8350_IRQ_GPIO(0): + return wm8350_clear_bits(wm8350, + WM8350_GPIO_INT_STATUS_MASK, + WM8350_IM_GP0_EINT); + case WM8350_IRQ_GPIO(1): + return wm8350_clear_bits(wm8350, + WM8350_GPIO_INT_STATUS_MASK, + WM8350_IM_GP1_EINT); + case WM8350_IRQ_GPIO(2): + return wm8350_clear_bits(wm8350, + WM8350_GPIO_INT_STATUS_MASK, + WM8350_IM_GP2_EINT); + case WM8350_IRQ_GPIO(3): + return wm8350_clear_bits(wm8350, + WM8350_GPIO_INT_STATUS_MASK, + WM8350_IM_GP3_EINT); + case WM8350_IRQ_GPIO(4): + return wm8350_clear_bits(wm8350, + WM8350_GPIO_INT_STATUS_MASK, + WM8350_IM_GP4_EINT); + case WM8350_IRQ_GPIO(5): + return wm8350_clear_bits(wm8350, + WM8350_GPIO_INT_STATUS_MASK, + WM8350_IM_GP5_EINT); + case WM8350_IRQ_GPIO(6): + return wm8350_clear_bits(wm8350, + WM8350_GPIO_INT_STATUS_MASK, + WM8350_IM_GP6_EINT); + case WM8350_IRQ_GPIO(7): + return wm8350_clear_bits(wm8350, + WM8350_GPIO_INT_STATUS_MASK, + WM8350_IM_GP7_EINT); + case WM8350_IRQ_GPIO(8): + return wm8350_clear_bits(wm8350, + WM8350_GPIO_INT_STATUS_MASK, + WM8350_IM_GP8_EINT); + case WM8350_IRQ_GPIO(9): + return wm8350_clear_bits(wm8350, + WM8350_GPIO_INT_STATUS_MASK, + WM8350_IM_GP9_EINT); + case WM8350_IRQ_GPIO(10): + return wm8350_clear_bits(wm8350, + WM8350_GPIO_INT_STATUS_MASK, + WM8350_IM_GP10_EINT); + case WM8350_IRQ_GPIO(11): + return wm8350_clear_bits(wm8350, + WM8350_GPIO_INT_STATUS_MASK, + WM8350_IM_GP11_EINT); + case WM8350_IRQ_GPIO(12): + return wm8350_clear_bits(wm8350, + WM8350_GPIO_INT_STATUS_MASK, + WM8350_IM_GP12_EINT); + default: + dev_warn(wm8350->dev, "Attempting to unmask unknown IRQ %d\n", + irq); + return -EINVAL; + } + return 0; +} +EXPORT_SYMBOL_GPL(wm8350_unmask_irq); + +/* + * Cache is always host endian. + */ +static int wm8350_create_cache(struct wm8350 *wm8350, int mode) +{ + int i, ret = 0; + u16 value; + const u16 *reg_map; + + switch (mode) { +#ifdef CONFIG_MFD_WM8350_CONFIG_MODE_0 + case 0: + reg_map = wm8350_mode0_defaults; + break; +#endif +#ifdef CONFIG_MFD_WM8350_CONFIG_MODE_1 + case 1: + reg_map = wm8350_mode1_defaults; + break; +#endif +#ifdef CONFIG_MFD_WM8350_CONFIG_MODE_2 + case 2: + reg_map = wm8350_mode2_defaults; + break; +#endif +#ifdef CONFIG_MFD_WM8350_CONFIG_MODE_3 + case 3: + reg_map = wm8350_mode3_defaults; + break; +#endif + default: + dev_err(wm8350->dev, "Configuration mode %d not supported\n", + mode); + return -EINVAL; + } + + wm8350->reg_cache = + kzalloc(sizeof(u16) * (WM8350_MAX_REGISTER + 1), GFP_KERNEL); + if (wm8350->reg_cache == NULL) + return -ENOMEM; + + /* Read the initial cache state back from the device - this is + * a PMIC so the device many not be in a virgin state and we + * can't rely on the silicon values. + */ + for (i = 0; i < WM8350_MAX_REGISTER; i++) { + /* audio register range */ + if (wm8350_reg_io_map[i].readable && + (i < WM8350_CLOCK_CONTROL_1 || i > WM8350_AIF_TEST)) { + ret = wm8350->read_dev(wm8350, i, 2, (char *)&value); + if (ret < 0) { + dev_err(wm8350->dev, + "failed to read initial cache value\n"); + goto out; + } + value = be16_to_cpu(value); + value &= wm8350_reg_io_map[i].readable; + wm8350->reg_cache[i] = value; + } else + wm8350->reg_cache[i] = reg_map[i]; + } + +out: + return ret; +} +EXPORT_SYMBOL_GPL(wm8350_create_cache); + +/* + * Register a client device. This is non-fatal since there is no need to + * fail the entire device init due to a single platform device failing. + */ +static void wm8350_client_dev_register(struct wm8350 *wm8350, + const char *name, + struct platform_device **pdev) +{ + int ret; + + *pdev = platform_device_alloc(name, -1); + if (pdev == NULL) { + dev_err(wm8350->dev, "Failed to allocate %s\n", name); + return; + } + + (*pdev)->dev.parent = wm8350->dev; + platform_set_drvdata(*pdev, wm8350); + ret = platform_device_add(*pdev); + if (ret != 0) { + dev_err(wm8350->dev, "Failed to register %s: %d\n", name, ret); + platform_device_put(*pdev); + *pdev = NULL; + } +} + +int wm8350_device_init(struct wm8350 *wm8350, int irq, + struct wm8350_platform_data *pdata) +{ + int ret = -EINVAL; + u16 id1, id2, mask, mode; + + /* get WM8350 revision and config mode */ + wm8350->read_dev(wm8350, WM8350_RESET_ID, sizeof(id1), &id1); + wm8350->read_dev(wm8350, WM8350_ID, sizeof(id2), &id2); + + id1 = be16_to_cpu(id1); + id2 = be16_to_cpu(id2); + + if (id1 == 0x6143) { + switch ((id2 & WM8350_CHIP_REV_MASK) >> 12) { + case WM8350_REV_E: + dev_info(wm8350->dev, "Found Rev E device\n"); + wm8350->rev = WM8350_REV_E; + break; + case WM8350_REV_F: + dev_info(wm8350->dev, "Found Rev F device\n"); + wm8350->rev = WM8350_REV_F; + break; + case WM8350_REV_G: + dev_info(wm8350->dev, "Found Rev G device\n"); + wm8350->rev = WM8350_REV_G; + break; + default: + /* For safety we refuse to run on unknown hardware */ + dev_info(wm8350->dev, "Found unknown rev\n"); + ret = -ENODEV; + goto err; + } + } else { + dev_info(wm8350->dev, "Device with ID %x is not a WM8350\n", + id1); + ret = -ENODEV; + goto err; + } + + mode = id2 & WM8350_CONF_STS_MASK >> 10; + mask = id2 & WM8350_CUST_ID_MASK; + dev_info(wm8350->dev, "Config mode %d, ROM mask %d\n", mode, mask); + + ret = wm8350_create_cache(wm8350, mode); + if (ret < 0) { + printk(KERN_ERR "wm8350: failed to create register cache\n"); + return ret; + } + + if (pdata->init) { + ret = pdata->init(wm8350); + if (ret != 0) { + dev_err(wm8350->dev, "Platform init() failed: %d\n", + ret); + goto err; + } + } + + mutex_init(&wm8350->irq_mutex); + INIT_WORK(&wm8350->irq_work, wm8350_irq_worker); + if (irq != NO_IRQ) { + ret = request_irq(irq, wm8350_irq, 0, + "wm8350", wm8350); + if (ret != 0) { + dev_err(wm8350->dev, "Failed to request IRQ: %d\n", + ret); + goto err; + } + } else { + dev_err(wm8350->dev, "No IRQ configured\n"); + goto err; + } + wm8350->chip_irq = irq; + + wm8350_reg_write(wm8350, WM8350_SYSTEM_INTERRUPTS_MASK, 0x0); + + wm8350_client_dev_register(wm8350, "wm8350-codec", + &(wm8350->codec.pdev)); + wm8350_client_dev_register(wm8350, "wm8350-gpio", + &(wm8350->gpio.pdev)); + wm8350_client_dev_register(wm8350, "wm8350-power", + &(wm8350->power.pdev)); + wm8350_client_dev_register(wm8350, "wm8350-rtc", &(wm8350->rtc.pdev)); + wm8350_client_dev_register(wm8350, "wm8350-wdt", &(wm8350->wdt.pdev)); + + return 0; + +err: + kfree(wm8350->reg_cache); + return ret; +} +EXPORT_SYMBOL_GPL(wm8350_device_init); + +void wm8350_device_exit(struct wm8350 *wm8350) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(wm8350->pmic.pdev); i++) + platform_device_unregister(wm8350->pmic.pdev[i]); + + platform_device_unregister(wm8350->wdt.pdev); + platform_device_unregister(wm8350->rtc.pdev); + platform_device_unregister(wm8350->power.pdev); + platform_device_unregister(wm8350->gpio.pdev); + platform_device_unregister(wm8350->codec.pdev); + + free_irq(wm8350->chip_irq, wm8350); + flush_work(&wm8350->irq_work); + kfree(wm8350->reg_cache); +} +EXPORT_SYMBOL_GPL(wm8350_device_exit); + +MODULE_DESCRIPTION("WM8350 AudioPlus PMIC core driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/mfd/wm8350-gpio.c b/drivers/mfd/wm8350-gpio.c new file mode 100644 index 000000000000..ebf99bef392f --- /dev/null +++ b/drivers/mfd/wm8350-gpio.c @@ -0,0 +1,222 @@ +/* + * wm8350-core.c -- Device access for Wolfson WM8350 + * + * Copyright 2007, 2008 Wolfson Microelectronics PLC. + * + * Author: Liam Girdwood + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + */ + +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/errno.h> + +#include <linux/mfd/wm8350/core.h> +#include <linux/mfd/wm8350/gpio.h> +#include <linux/mfd/wm8350/pmic.h> + +static int gpio_set_dir(struct wm8350 *wm8350, int gpio, int dir) +{ + int ret; + + wm8350_reg_unlock(wm8350); + if (dir == WM8350_GPIO_DIR_OUT) + ret = wm8350_clear_bits(wm8350, + WM8350_GPIO_CONFIGURATION_I_O, + 1 << gpio); + else + ret = wm8350_set_bits(wm8350, + WM8350_GPIO_CONFIGURATION_I_O, + 1 << gpio); + wm8350_reg_lock(wm8350); + return ret; +} + +static int gpio_set_debounce(struct wm8350 *wm8350, int gpio, int db) +{ + if (db == WM8350_GPIO_DEBOUNCE_ON) + return wm8350_set_bits(wm8350, WM8350_GPIO_DEBOUNCE, + 1 << gpio); + else + return wm8350_clear_bits(wm8350, + WM8350_GPIO_DEBOUNCE, 1 << gpio); +} + +static int gpio_set_func(struct wm8350 *wm8350, int gpio, int func) +{ + u16 reg; + + wm8350_reg_unlock(wm8350); + switch (gpio) { + case 0: + reg = wm8350_reg_read(wm8350, WM8350_GPIO_FUNCTION_SELECT_1) + & ~WM8350_GP0_FN_MASK; + wm8350_reg_write(wm8350, WM8350_GPIO_FUNCTION_SELECT_1, + reg | ((func & 0xf) << 0)); + break; + case 1: + reg = wm8350_reg_read(wm8350, WM8350_GPIO_FUNCTION_SELECT_1) + & ~WM8350_GP1_FN_MASK; + wm8350_reg_write(wm8350, WM8350_GPIO_FUNCTION_SELECT_1, + reg | ((func & 0xf) << 4)); + break; + case 2: + reg = wm8350_reg_read(wm8350, WM8350_GPIO_FUNCTION_SELECT_1) + & ~WM8350_GP2_FN_MASK; + wm8350_reg_write(wm8350, WM8350_GPIO_FUNCTION_SELECT_1, + reg | ((func & 0xf) << 8)); + break; + case 3: + reg = wm8350_reg_read(wm8350, WM8350_GPIO_FUNCTION_SELECT_1) + & ~WM8350_GP3_FN_MASK; + wm8350_reg_write(wm8350, WM8350_GPIO_FUNCTION_SELECT_1, + reg | ((func & 0xf) << 12)); + break; + case 4: + reg = wm8350_reg_read(wm8350, WM8350_GPIO_FUNCTION_SELECT_2) + & ~WM8350_GP4_FN_MASK; + wm8350_reg_write(wm8350, WM8350_GPIO_FUNCTION_SELECT_2, + reg | ((func & 0xf) << 0)); + break; + case 5: + reg = wm8350_reg_read(wm8350, WM8350_GPIO_FUNCTION_SELECT_2) + & ~WM8350_GP5_FN_MASK; + wm8350_reg_write(wm8350, WM8350_GPIO_FUNCTION_SELECT_2, + reg | ((func & 0xf) << 4)); + break; + case 6: + reg = wm8350_reg_read(wm8350, WM8350_GPIO_FUNCTION_SELECT_2) + & ~WM8350_GP6_FN_MASK; + wm8350_reg_write(wm8350, WM8350_GPIO_FUNCTION_SELECT_2, + reg | ((func & 0xf) << 8)); + break; + case 7: + reg = wm8350_reg_read(wm8350, WM8350_GPIO_FUNCTION_SELECT_2) + & ~WM8350_GP7_FN_MASK; + wm8350_reg_write(wm8350, WM8350_GPIO_FUNCTION_SELECT_2, + reg | ((func & 0xf) << 12)); + break; + case 8: + reg = wm8350_reg_read(wm8350, WM8350_GPIO_FUNCTION_SELECT_3) + & ~WM8350_GP8_FN_MASK; + wm8350_reg_write(wm8350, WM8350_GPIO_FUNCTION_SELECT_3, + reg | ((func & 0xf) << 0)); + break; + case 9: + reg = wm8350_reg_read(wm8350, WM8350_GPIO_FUNCTION_SELECT_3) + & ~WM8350_GP9_FN_MASK; + wm8350_reg_write(wm8350, WM8350_GPIO_FUNCTION_SELECT_3, + reg | ((func & 0xf) << 4)); + break; + case 10: + reg = wm8350_reg_read(wm8350, WM8350_GPIO_FUNCTION_SELECT_3) + & ~WM8350_GP10_FN_MASK; + wm8350_reg_write(wm8350, WM8350_GPIO_FUNCTION_SELECT_3, + reg | ((func & 0xf) << 8)); + break; + case 11: + reg = wm8350_reg_read(wm8350, WM8350_GPIO_FUNCTION_SELECT_3) + & ~WM8350_GP11_FN_MASK; + wm8350_reg_write(wm8350, WM8350_GPIO_FUNCTION_SELECT_3, + reg | ((func & 0xf) << 12)); + break; + case 12: + reg = wm8350_reg_read(wm8350, WM8350_GPIO_FUNCTION_SELECT_4) + & ~WM8350_GP12_FN_MASK; + wm8350_reg_write(wm8350, WM8350_GPIO_FUNCTION_SELECT_4, + reg | ((func & 0xf) << 0)); + break; + default: + wm8350_reg_lock(wm8350); + return -EINVAL; + } + + wm8350_reg_lock(wm8350); + return 0; +} + +static int gpio_set_pull_up(struct wm8350 *wm8350, int gpio, int up) +{ + if (up) + return wm8350_set_bits(wm8350, + WM8350_GPIO_PIN_PULL_UP_CONTROL, + 1 << gpio); + else + return wm8350_clear_bits(wm8350, + WM8350_GPIO_PIN_PULL_UP_CONTROL, + 1 << gpio); +} + +static int gpio_set_pull_down(struct wm8350 *wm8350, int gpio, int down) +{ + if (down) + return wm8350_set_bits(wm8350, + WM8350_GPIO_PULL_DOWN_CONTROL, + 1 << gpio); + else + return wm8350_clear_bits(wm8350, + WM8350_GPIO_PULL_DOWN_CONTROL, + 1 << gpio); +} + +static int gpio_set_polarity(struct wm8350 *wm8350, int gpio, int pol) +{ + if (pol == WM8350_GPIO_ACTIVE_HIGH) + return wm8350_set_bits(wm8350, + WM8350_GPIO_PIN_POLARITY_TYPE, + 1 << gpio); + else + return wm8350_clear_bits(wm8350, + WM8350_GPIO_PIN_POLARITY_TYPE, + 1 << gpio); +} + +static int gpio_set_invert(struct wm8350 *wm8350, int gpio, int invert) +{ + if (invert == WM8350_GPIO_INVERT_ON) + return wm8350_set_bits(wm8350, WM8350_GPIO_INT_MODE, 1 << gpio); + else + return wm8350_clear_bits(wm8350, + WM8350_GPIO_INT_MODE, 1 << gpio); +} + +int wm8350_gpio_config(struct wm8350 *wm8350, int gpio, int dir, int func, + int pol, int pull, int invert, int debounce) +{ + /* make sure we never pull up and down at the same time */ + if (pull == WM8350_GPIO_PULL_NONE) { + if (gpio_set_pull_up(wm8350, gpio, 0)) + goto err; + if (gpio_set_pull_down(wm8350, gpio, 0)) + goto err; + } else if (pull == WM8350_GPIO_PULL_UP) { + if (gpio_set_pull_down(wm8350, gpio, 0)) + goto err; + if (gpio_set_pull_up(wm8350, gpio, 1)) + goto err; + } else if (pull == WM8350_GPIO_PULL_DOWN) { + if (gpio_set_pull_up(wm8350, gpio, 0)) + goto err; + if (gpio_set_pull_down(wm8350, gpio, 1)) + goto err; + } + + if (gpio_set_invert(wm8350, gpio, invert)) + goto err; + if (gpio_set_polarity(wm8350, gpio, pol)) + goto err; + if (gpio_set_debounce(wm8350, gpio, debounce)) + goto err; + if (gpio_set_dir(wm8350, gpio, dir)) + goto err; + return gpio_set_func(wm8350, gpio, func); + +err: + return -EIO; +} +EXPORT_SYMBOL_GPL(wm8350_gpio_config); diff --git a/drivers/mfd/wm8350-i2c.c b/drivers/mfd/wm8350-i2c.c new file mode 100644 index 000000000000..8dfe21bb3bd1 --- /dev/null +++ b/drivers/mfd/wm8350-i2c.c @@ -0,0 +1,120 @@ +/* + * wm8350-i2c.c -- Generic I2C driver for Wolfson WM8350 PMIC + * + * This driver defines and configures the WM8350 for the Freescale i.MX32ADS. + * + * Copyright 2007, 2008 Wolfson Microelectronics PLC. + * + * Author: Liam Girdwood + * linux@wolfsonmicro.com + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + */ + +#include <linux/module.h> +#include <linux/moduleparam.h> +#include <linux/init.h> +#include <linux/i2c.h> +#include <linux/platform_device.h> +#include <linux/mfd/wm8350/core.h> + +static int wm8350_i2c_read_device(struct wm8350 *wm8350, char reg, + int bytes, void *dest) +{ + int ret; + + ret = i2c_master_send(wm8350->i2c_client, ®, 1); + if (ret < 0) + return ret; + return i2c_master_recv(wm8350->i2c_client, dest, bytes); +} + +static int wm8350_i2c_write_device(struct wm8350 *wm8350, char reg, + int bytes, void *src) +{ + /* we add 1 byte for device register */ + u8 msg[(WM8350_MAX_REGISTER << 1) + 1]; + + if (bytes > ((WM8350_MAX_REGISTER << 1) + 1)) + return -EINVAL; + + msg[0] = reg; + memcpy(&msg[1], src, bytes); + return i2c_master_send(wm8350->i2c_client, msg, bytes + 1); +} + +static int wm8350_i2c_probe(struct i2c_client *i2c, + const struct i2c_device_id *id) +{ + struct wm8350 *wm8350; + int ret = 0; + + wm8350 = kzalloc(sizeof(struct wm8350), GFP_KERNEL); + if (wm8350 == NULL) { + kfree(i2c); + return -ENOMEM; + } + + i2c_set_clientdata(i2c, wm8350); + wm8350->dev = &i2c->dev; + wm8350->i2c_client = i2c; + wm8350->read_dev = wm8350_i2c_read_device; + wm8350->write_dev = wm8350_i2c_write_device; + + ret = wm8350_device_init(wm8350, i2c->irq, i2c->dev.platform_data); + if (ret < 0) + goto err; + + return ret; + +err: + kfree(wm8350); + return ret; +} + +static int wm8350_i2c_remove(struct i2c_client *i2c) +{ + struct wm8350 *wm8350 = i2c_get_clientdata(i2c); + + wm8350_device_exit(wm8350); + kfree(wm8350); + + return 0; +} + +static const struct i2c_device_id wm8350_i2c_id[] = { + { "wm8350", 0 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, wm8350_i2c_id); + + +static struct i2c_driver wm8350_i2c_driver = { + .driver = { + .name = "wm8350", + .owner = THIS_MODULE, + }, + .probe = wm8350_i2c_probe, + .remove = wm8350_i2c_remove, + .id_table = wm8350_i2c_id, +}; + +static int __init wm8350_i2c_init(void) +{ + return i2c_add_driver(&wm8350_i2c_driver); +} +/* init early so consumer devices can complete system boot */ +subsys_initcall(wm8350_i2c_init); + +static void __exit wm8350_i2c_exit(void) +{ + i2c_del_driver(&wm8350_i2c_driver); +} +module_exit(wm8350_i2c_exit); + +MODULE_DESCRIPTION("I2C support for the WM8350 AudioPlus PMIC"); +MODULE_LICENSE("GPL"); diff --git a/drivers/mfd/wm8350-regmap.c b/drivers/mfd/wm8350-regmap.c new file mode 100644 index 000000000000..974678db22cd --- /dev/null +++ b/drivers/mfd/wm8350-regmap.c @@ -0,0 +1,1347 @@ +/* + * wm8350-regmap.c -- Wolfson Microelectronics WM8350 register map + * + * This file splits out the tables describing the defaults and access + * status of the WM8350 registers since they are rather large. + * + * Copyright 2007, 2008 Wolfson Microelectronics PLC. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + */ + +#include <linux/mfd/wm8350/core.h> + +#ifdef CONFIG_MFD_WM8350_CONFIG_MODE_0 + +#undef WM8350_HAVE_CONFIG_MODE +#define WM8350_HAVE_CONFIG_MODE + +const u16 wm8350_mode0_defaults[] = { + 0x17FF, /* R0 - Reset/ID */ + 0x1000, /* R1 - ID */ + 0x0000, /* R2 */ + 0x1002, /* R3 - System Control 1 */ + 0x0004, /* R4 - System Control 2 */ + 0x0000, /* R5 - System Hibernate */ + 0x8A00, /* R6 - Interface Control */ + 0x0000, /* R7 */ + 0x8000, /* R8 - Power mgmt (1) */ + 0x0000, /* R9 - Power mgmt (2) */ + 0x0000, /* R10 - Power mgmt (3) */ + 0x2000, /* R11 - Power mgmt (4) */ + 0x0E00, /* R12 - Power mgmt (5) */ + 0x0000, /* R13 - Power mgmt (6) */ + 0x0000, /* R14 - Power mgmt (7) */ + 0x0000, /* R15 */ + 0x0000, /* R16 - RTC Seconds/Minutes */ + 0x0100, /* R17 - RTC Hours/Day */ + 0x0101, /* R18 - RTC Date/Month */ + 0x1400, /* R19 - RTC Year */ + 0x0000, /* R20 - Alarm Seconds/Minutes */ + 0x0000, /* R21 - Alarm Hours/Day */ + 0x0000, /* R22 - Alarm Date/Month */ + 0x0320, /* R23 - RTC Time Control */ + 0x0000, /* R24 - System Interrupts */ + 0x0000, /* R25 - Interrupt Status 1 */ + 0x0000, /* R26 - Interrupt Status 2 */ + 0x0000, /* R27 - Power Up Interrupt Status */ + 0x0000, /* R28 - Under Voltage Interrupt status */ + 0x0000, /* R29 - Over Current Interrupt status */ + 0x0000, /* R30 - GPIO Interrupt Status */ + 0x0000, /* R31 - Comparator Interrupt Status */ + 0x3FFF, /* R32 - System Interrupts Mask */ + 0x0000, /* R33 - Interrupt Status 1 Mask */ + 0x0000, /* R34 - Interrupt Status 2 Mask */ + 0x0000, /* R35 - Power Up Interrupt Status Mask */ + 0x0000, /* R36 - Under Voltage Interrupt status Mask */ + 0x0000, /* R37 - Over Current Interrupt status Mask */ + 0x0000, /* R38 - GPIO Interrupt Status Mask */ + 0x0000, /* R39 - Comparator Interrupt Status Mask */ + 0x0040, /* R40 - Clock Control 1 */ + 0x0000, /* R41 - Clock Control 2 */ + 0x3B00, /* R42 - FLL Control 1 */ + 0x7086, /* R43 - FLL Control 2 */ + 0xC226, /* R44 - FLL Control 3 */ + 0x0000, /* R45 - FLL Control 4 */ + 0x0000, /* R46 */ + 0x0000, /* R47 */ + 0x0000, /* R48 - DAC Control */ + 0x0000, /* R49 */ + 0x00C0, /* R50 - DAC Digital Volume L */ + 0x00C0, /* R51 - DAC Digital Volume R */ + 0x0000, /* R52 */ + 0x0040, /* R53 - DAC LR Rate */ + 0x0000, /* R54 - DAC Clock Control */ + 0x0000, /* R55 */ + 0x0000, /* R56 */ + 0x0000, /* R57 */ + 0x4000, /* R58 - DAC Mute */ + 0x0000, /* R59 - DAC Mute Volume */ + 0x0000, /* R60 - DAC Side */ + 0x0000, /* R61 */ + 0x0000, /* R62 */ + 0x0000, /* R63 */ + 0x8000, /* R64 - ADC Control */ + 0x0000, /* R65 */ + 0x00C0, /* R66 - ADC Digital Volume L */ + 0x00C0, /* R67 - ADC Digital Volume R */ + 0x0000, /* R68 - ADC Divider */ + 0x0000, /* R69 */ + 0x0040, /* R70 - ADC LR Rate */ + 0x0000, /* R71 */ + 0x0303, /* R72 - Input Control */ + 0x0000, /* R73 - IN3 Input Control */ + 0x0000, /* R74 - Mic Bias Control */ + 0x0000, /* R75 */ + 0x0000, /* R76 - Output Control */ + 0x0000, /* R77 - Jack Detect */ + 0x0000, /* R78 - Anti Pop Control */ + 0x0000, /* R79 */ + 0x0040, /* R80 - Left Input Volume */ + 0x0040, /* R81 - Right Input Volume */ + 0x0000, /* R82 */ + 0x0000, /* R83 */ + 0x0000, /* R84 */ + 0x0000, /* R85 */ + 0x0000, /* R86 */ + 0x0000, /* R87 */ + 0x0800, /* R88 - Left Mixer Control */ + 0x1000, /* R89 - Right Mixer Control */ + 0x0000, /* R90 */ + 0x0000, /* R91 */ + 0x0000, /* R92 - OUT3 Mixer Control */ + 0x0000, /* R93 - OUT4 Mixer Control */ + 0x0000, /* R94 */ + 0x0000, /* R95 */ + 0x0000, /* R96 - Output Left Mixer Volume */ + 0x0000, /* R97 - Output Right Mixer Volume */ + 0x0000, /* R98 - Input Mixer Volume L */ + 0x0000, /* R99 - Input Mixer Volume R */ + 0x0000, /* R100 - Input Mixer Volume */ + 0x0000, /* R101 */ + 0x0000, /* R102 */ + 0x0000, /* R103 */ + 0x00E4, /* R104 - LOUT1 Volume */ + 0x00E4, /* R105 - ROUT1 Volume */ + 0x00E4, /* R106 - LOUT2 Volume */ + 0x02E4, /* R107 - ROUT2 Volume */ + 0x0000, /* R108 */ + 0x0000, /* R109 */ + 0x0000, /* R110 */ + 0x0000, /* R111 - BEEP Volume */ + 0x0A00, /* R112 - AI Formating */ + 0x0000, /* R113 - ADC DAC COMP */ + 0x0020, /* R114 - AI ADC Control */ + 0x0020, /* R115 - AI DAC Control */ + 0x0000, /* R116 - AIF Test */ + 0x0000, /* R117 */ + 0x0000, /* R118 */ + 0x0000, /* R119 */ + 0x0000, /* R120 */ + 0x0000, /* R121 */ + 0x0000, /* R122 */ + 0x0000, /* R123 */ + 0x0000, /* R124 */ + 0x0000, /* R125 */ + 0x0000, /* R126 */ + 0x0000, /* R127 */ + 0x1FFF, /* R128 - GPIO Debounce */ + 0x0000, /* R129 - GPIO Pin pull up Control */ + 0x03FC, /* R130 - GPIO Pull down Control */ + 0x0000, /* R131 - GPIO Interrupt Mode */ + 0x0000, /* R132 */ + 0x0000, /* R133 - GPIO Control */ + 0x0FFC, /* R134 - GPIO Configuration (i/o) */ + 0x0FFC, /* R135 - GPIO Pin Polarity / Type */ + 0x0000, /* R136 */ + 0x0000, /* R137 */ + 0x0000, /* R138 */ + 0x0000, /* R139 */ + 0x0013, /* R140 - GPIO Function Select 1 */ + 0x0000, /* R141 - GPIO Function Select 2 */ + 0x0000, /* R142 - GPIO Function Select 3 */ + 0x0003, /* R143 - GPIO Function Select 4 */ + 0x0000, /* R144 - Digitiser Control (1) */ + 0x0002, /* R145 - Digitiser Control (2) */ + 0x0000, /* R146 */ + 0x0000, /* R147 */ + 0x0000, /* R148 */ + 0x0000, /* R149 */ + 0x0000, /* R150 */ + 0x0000, /* R151 */ + 0x7000, /* R152 - AUX1 Readback */ + 0x7000, /* R153 - AUX2 Readback */ + 0x7000, /* R154 - AUX3 Readback */ + 0x7000, /* R155 - AUX4 Readback */ + 0x0000, /* R156 - USB Voltage Readback */ + 0x0000, /* R157 - LINE Voltage Readback */ + 0x0000, /* R158 - BATT Voltage Readback */ + 0x0000, /* R159 - Chip Temp Readback */ + 0x0000, /* R160 */ + 0x0000, /* R161 */ + 0x0000, /* R162 */ + 0x0000, /* R163 - Generic Comparator Control */ + 0x0000, /* R164 - Generic comparator 1 */ + 0x0000, /* R165 - Generic comparator 2 */ + 0x0000, /* R166 - Generic comparator 3 */ + 0x0000, /* R167 - Generic comparator 4 */ + 0xA00F, /* R168 - Battery Charger Control 1 */ + 0x0B06, /* R169 - Battery Charger Control 2 */ + 0x0000, /* R170 - Battery Charger Control 3 */ + 0x0000, /* R171 */ + 0x0000, /* R172 - Current Sink Driver A */ + 0x0000, /* R173 - CSA Flash control */ + 0x0000, /* R174 - Current Sink Driver B */ + 0x0000, /* R175 - CSB Flash control */ + 0x0000, /* R176 - DCDC/LDO requested */ + 0x002D, /* R177 - DCDC Active options */ + 0x0000, /* R178 - DCDC Sleep options */ + 0x0025, /* R179 - Power-check comparator */ + 0x000E, /* R180 - DCDC1 Control */ + 0x0000, /* R181 - DCDC1 Timeouts */ + 0x1006, /* R182 - DCDC1 Low Power */ + 0x0018, /* R183 - DCDC2 Control */ + 0x0000, /* R184 - DCDC2 Timeouts */ + 0x0000, /* R185 */ + 0x0000, /* R186 - DCDC3 Control */ + 0x0000, /* R187 - DCDC3 Timeouts */ + 0x0006, /* R188 - DCDC3 Low Power */ + 0x0000, /* R189 - DCDC4 Control */ + 0x0000, /* R190 - DCDC4 Timeouts */ + 0x0006, /* R191 - DCDC4 Low Power */ + 0x0008, /* R192 - DCDC5 Control */ + 0x0000, /* R193 - DCDC5 Timeouts */ + 0x0000, /* R194 */ + 0x0000, /* R195 - DCDC6 Control */ + 0x0000, /* R196 - DCDC6 Timeouts */ + 0x0006, /* R197 - DCDC6 Low Power */ + 0x0000, /* R198 */ + 0x0003, /* R199 - Limit Switch Control */ + 0x001C, /* R200 - LDO1 Control */ + 0x0000, /* R201 - LDO1 Timeouts */ + 0x001C, /* R202 - LDO1 Low Power */ + 0x001B, /* R203 - LDO2 Control */ + 0x0000, /* R204 - LDO2 Timeouts */ + 0x001C, /* R205 - LDO2 Low Power */ + 0x001B, /* R206 - LDO3 Control */ + 0x0000, /* R207 - LDO3 Timeouts */ + 0x001C, /* R208 - LDO3 Low Power */ + 0x001B, /* R209 - LDO4 Control */ + 0x0000, /* R210 - LDO4 Timeouts */ + 0x001C, /* R211 - LDO4 Low Power */ + 0x0000, /* R212 */ + 0x0000, /* R213 */ + 0x0000, /* R214 */ + 0x0000, /* R215 - VCC_FAULT Masks */ + 0x001F, /* R216 - Main Bandgap Control */ + 0x0000, /* R217 - OSC Control */ + 0x9000, /* R218 - RTC Tick Control */ + 0x0000, /* R219 */ + 0x4000, /* R220 - RAM BIST 1 */ + 0x0000, /* R221 */ + 0x0000, /* R222 */ + 0x0000, /* R223 */ + 0x0000, /* R224 */ + 0x0000, /* R225 - DCDC/LDO status */ + 0x0000, /* R226 */ + 0x0000, /* R227 */ + 0x0000, /* R228 */ + 0x0000, /* R229 */ + 0xE000, /* R230 - GPIO Pin Status */ + 0x0000, /* R231 */ + 0x0000, /* R232 */ + 0x0000, /* R233 */ + 0x0000, /* R234 */ + 0x0000, /* R235 */ + 0x0000, /* R236 */ + 0x0000, /* R237 */ + 0x0000, /* R238 */ + 0x0000, /* R239 */ + 0x0000, /* R240 */ + 0x0000, /* R241 */ + 0x0000, /* R242 */ + 0x0000, /* R243 */ + 0x0000, /* R244 */ + 0x0000, /* R245 */ + 0x0000, /* R246 */ + 0x0000, /* R247 */ + 0x0000, /* R248 */ + 0x0000, /* R249 */ + 0x0000, /* R250 */ + 0x0000, /* R251 */ + 0x0000, /* R252 */ + 0x0000, /* R253 */ + 0x0000, /* R254 */ + 0x0000, /* R255 */ +}; +#endif + +#ifdef CONFIG_MFD_WM8350_CONFIG_MODE_1 + +#undef WM8350_HAVE_CONFIG_MODE +#define WM8350_HAVE_CONFIG_MODE + +const u16 wm8350_mode1_defaults[] = { + 0x17FF, /* R0 - Reset/ID */ + 0x1000, /* R1 - ID */ + 0x0000, /* R2 */ + 0x1002, /* R3 - System Control 1 */ + 0x0014, /* R4 - System Control 2 */ + 0x0000, /* R5 - System Hibernate */ + 0x8A00, /* R6 - Interface Control */ + 0x0000, /* R7 */ + 0x8000, /* R8 - Power mgmt (1) */ + 0x0000, /* R9 - Power mgmt (2) */ + 0x0000, /* R10 - Power mgmt (3) */ + 0x2000, /* R11 - Power mgmt (4) */ + 0x0E00, /* R12 - Power mgmt (5) */ + 0x0000, /* R13 - Power mgmt (6) */ + 0x0000, /* R14 - Power mgmt (7) */ + 0x0000, /* R15 */ + 0x0000, /* R16 - RTC Seconds/Minutes */ + 0x0100, /* R17 - RTC Hours/Day */ + 0x0101, /* R18 - RTC Date/Month */ + 0x1400, /* R19 - RTC Year */ + 0x0000, /* R20 - Alarm Seconds/Minutes */ + 0x0000, /* R21 - Alarm Hours/Day */ + 0x0000, /* R22 - Alarm Date/Month */ + 0x0320, /* R23 - RTC Time Control */ + 0x0000, /* R24 - System Interrupts */ + 0x0000, /* R25 - Interrupt Status 1 */ + 0x0000, /* R26 - Interrupt Status 2 */ + 0x0000, /* R27 - Power Up Interrupt Status */ + 0x0000, /* R28 - Under Voltage Interrupt status */ + 0x0000, /* R29 - Over Current Interrupt status */ + 0x0000, /* R30 - GPIO Interrupt Status */ + 0x0000, /* R31 - Comparator Interrupt Status */ + 0x3FFF, /* R32 - System Interrupts Mask */ + 0x0000, /* R33 - Interrupt Status 1 Mask */ + 0x0000, /* R34 - Interrupt Status 2 Mask */ + 0x0000, /* R35 - Power Up Interrupt Status Mask */ + 0x0000, /* R36 - Under Voltage Interrupt status Mask */ + 0x0000, /* R37 - Over Current Interrupt status Mask */ + 0x0000, /* R38 - GPIO Interrupt Status Mask */ + 0x0000, /* R39 - Comparator Interrupt Status Mask */ + 0x0040, /* R40 - Clock Control 1 */ + 0x0000, /* R41 - Clock Control 2 */ + 0x3B00, /* R42 - FLL Control 1 */ + 0x7086, /* R43 - FLL Control 2 */ + 0xC226, /* R44 - FLL Control 3 */ + 0x0000, /* R45 - FLL Control 4 */ + 0x0000, /* R46 */ + 0x0000, /* R47 */ + 0x0000, /* R48 - DAC Control */ + 0x0000, /* R49 */ + 0x00C0, /* R50 - DAC Digital Volume L */ + 0x00C0, /* R51 - DAC Digital Volume R */ + 0x0000, /* R52 */ + 0x0040, /* R53 - DAC LR Rate */ + 0x0000, /* R54 - DAC Clock Control */ + 0x0000, /* R55 */ + 0x0000, /* R56 */ + 0x0000, /* R57 */ + 0x4000, /* R58 - DAC Mute */ + 0x0000, /* R59 - DAC Mute Volume */ + 0x0000, /* R60 - DAC Side */ + 0x0000, /* R61 */ + 0x0000, /* R62 */ + 0x0000, /* R63 */ + 0x8000, /* R64 - ADC Control */ + 0x0000, /* R65 */ + 0x00C0, /* R66 - ADC Digital Volume L */ + 0x00C0, /* R67 - ADC Digital Volume R */ + 0x0000, /* R68 - ADC Divider */ + 0x0000, /* R69 */ + 0x0040, /* R70 - ADC LR Rate */ + 0x0000, /* R71 */ + 0x0303, /* R72 - Input Control */ + 0x0000, /* R73 - IN3 Input Control */ + 0x0000, /* R74 - Mic Bias Control */ + 0x0000, /* R75 */ + 0x0000, /* R76 - Output Control */ + 0x0000, /* R77 - Jack Detect */ + 0x0000, /* R78 - Anti Pop Control */ + 0x0000, /* R79 */ + 0x0040, /* R80 - Left Input Volume */ + 0x0040, /* R81 - Right Input Volume */ + 0x0000, /* R82 */ + 0x0000, /* R83 */ + 0x0000, /* R84 */ + 0x0000, /* R85 */ + 0x0000, /* R86 */ + 0x0000, /* R87 */ + 0x0800, /* R88 - Left Mixer Control */ + 0x1000, /* R89 - Right Mixer Control */ + 0x0000, /* R90 */ + 0x0000, /* R91 */ + 0x0000, /* R92 - OUT3 Mixer Control */ + 0x0000, /* R93 - OUT4 Mixer Control */ + 0x0000, /* R94 */ + 0x0000, /* R95 */ + 0x0000, /* R96 - Output Left Mixer Volume */ + 0x0000, /* R97 - Output Right Mixer Volume */ + 0x0000, /* R98 - Input Mixer Volume L */ + 0x0000, /* R99 - Input Mixer Volume R */ + 0x0000, /* R100 - Input Mixer Volume */ + 0x0000, /* R101 */ + 0x0000, /* R102 */ + 0x0000, /* R103 */ + 0x00E4, /* R104 - LOUT1 Volume */ + 0x00E4, /* R105 - ROUT1 Volume */ + 0x00E4, /* R106 - LOUT2 Volume */ + 0x02E4, /* R107 - ROUT2 Volume */ + 0x0000, /* R108 */ + 0x0000, /* R109 */ + 0x0000, /* R110 */ + 0x0000, /* R111 - BEEP Volume */ + 0x0A00, /* R112 - AI Formating */ + 0x0000, /* R113 - ADC DAC COMP */ + 0x0020, /* R114 - AI ADC Control */ + 0x0020, /* R115 - AI DAC Control */ + 0x0000, /* R116 - AIF Test */ + 0x0000, /* R117 */ + 0x0000, /* R118 */ + 0x0000, /* R119 */ + 0x0000, /* R120 */ + 0x0000, /* R121 */ + 0x0000, /* R122 */ + 0x0000, /* R123 */ + 0x0000, /* R124 */ + 0x0000, /* R125 */ + 0x0000, /* R126 */ + 0x0000, /* R127 */ + 0x1FFF, /* R128 - GPIO Debounce */ + 0x0000, /* R129 - GPIO Pin pull up Control */ + 0x03FC, /* R130 - GPIO Pull down Control */ + 0x0000, /* R131 - GPIO Interrupt Mode */ + 0x0000, /* R132 */ + 0x0000, /* R133 - GPIO Control */ + 0x00FB, /* R134 - GPIO Configuration (i/o) */ + 0x04FE, /* R135 - GPIO Pin Polarity / Type */ + 0x0000, /* R136 */ + 0x0000, /* R137 */ + 0x0000, /* R138 */ + 0x0000, /* R139 */ + 0x0312, /* R140 - GPIO Function Select 1 */ + 0x1003, /* R141 - GPIO Function Select 2 */ + 0x1331, /* R142 - GPIO Function Select 3 */ + 0x0003, /* R143 - GPIO Function Select 4 */ + 0x0000, /* R144 - Digitiser Control (1) */ + 0x0002, /* R145 - Digitiser Control (2) */ + 0x0000, /* R146 */ + 0x0000, /* R147 */ + 0x0000, /* R148 */ + 0x0000, /* R149 */ + 0x0000, /* R150 */ + 0x0000, /* R151 */ + 0x7000, /* R152 - AUX1 Readback */ + 0x7000, /* R153 - AUX2 Readback */ + 0x7000, /* R154 - AUX3 Readback */ + 0x7000, /* R155 - AUX4 Readback */ + 0x0000, /* R156 - USB Voltage Readback */ + 0x0000, /* R157 - LINE Voltage Readback */ + 0x0000, /* R158 - BATT Voltage Readback */ + 0x0000, /* R159 - Chip Temp Readback */ + 0x0000, /* R160 */ + 0x0000, /* R161 */ + 0x0000, /* R162 */ + 0x0000, /* R163 - Generic Comparator Control */ + 0x0000, /* R164 - Generic comparator 1 */ + 0x0000, /* R165 - Generic comparator 2 */ + 0x0000, /* R166 - Generic comparator 3 */ + 0x0000, /* R167 - Generic comparator 4 */ + 0xA00F, /* R168 - Battery Charger Control 1 */ + 0x0B06, /* R169 - Battery Charger Control 2 */ + 0x0000, /* R170 - Battery Charger Control 3 */ + 0x0000, /* R171 */ + 0x0000, /* R172 - Current Sink Driver A */ + 0x0000, /* R173 - CSA Flash control */ + 0x0000, /* R174 - Current Sink Driver B */ + 0x0000, /* R175 - CSB Flash control */ + 0x0000, /* R176 - DCDC/LDO requested */ + 0x002D, /* R177 - DCDC Active options */ + 0x0000, /* R178 - DCDC Sleep options */ + 0x0025, /* R179 - Power-check comparator */ + 0x0062, /* R180 - DCDC1 Control */ + 0x0400, /* R181 - DCDC1 Timeouts */ + 0x1006, /* R182 - DCDC1 Low Power */ + 0x0018, /* R183 - DCDC2 Control */ + 0x0000, /* R184 - DCDC2 Timeouts */ + 0x0000, /* R185 */ + 0x0026, /* R186 - DCDC3 Control */ + 0x0400, /* R187 - DCDC3 Timeouts */ + 0x0006, /* R188 - DCDC3 Low Power */ + 0x0062, /* R189 - DCDC4 Control */ + 0x0400, /* R190 - DCDC4 Timeouts */ + 0x0006, /* R191 - DCDC4 Low Power */ + 0x0008, /* R192 - DCDC5 Control */ + 0x0000, /* R193 - DCDC5 Timeouts */ + 0x0000, /* R194 */ + 0x0026, /* R195 - DCDC6 Control */ + 0x0800, /* R196 - DCDC6 Timeouts */ + 0x0006, /* R197 - DCDC6 Low Power */ + 0x0000, /* R198 */ + 0x0003, /* R199 - Limit Switch Control */ + 0x0006, /* R200 - LDO1 Control */ + 0x0400, /* R201 - LDO1 Timeouts */ + 0x001C, /* R202 - LDO1 Low Power */ + 0x0006, /* R203 - LDO2 Control */ + 0x0400, /* R204 - LDO2 Timeouts */ + 0x001C, /* R205 - LDO2 Low Power */ + 0x001B, /* R206 - LDO3 Control */ + 0x0000, /* R207 - LDO3 Timeouts */ + 0x001C, /* R208 - LDO3 Low Power */ + 0x001B, /* R209 - LDO4 Control */ + 0x0000, /* R210 - LDO4 Timeouts */ + 0x001C, /* R211 - LDO4 Low Power */ + 0x0000, /* R212 */ + 0x0000, /* R213 */ + 0x0000, /* R214 */ + 0x0000, /* R215 - VCC_FAULT Masks */ + 0x001F, /* R216 - Main Bandgap Control */ + 0x0000, /* R217 - OSC Control */ + 0x9000, /* R218 - RTC Tick Control */ + 0x0000, /* R219 */ + 0x4000, /* R220 - RAM BIST 1 */ + 0x0000, /* R221 */ + 0x0000, /* R222 */ + 0x0000, /* R223 */ + 0x0000, /* R224 */ + 0x0000, /* R225 - DCDC/LDO status */ + 0x0000, /* R226 */ + 0x0000, /* R227 */ + 0x0000, /* R228 */ + 0x0000, /* R229 */ + 0xE000, /* R230 - GPIO Pin Status */ + 0x0000, /* R231 */ + 0x0000, /* R232 */ + 0x0000, /* R233 */ + 0x0000, /* R234 */ + 0x0000, /* R235 */ + 0x0000, /* R236 */ + 0x0000, /* R237 */ + 0x0000, /* R238 */ + 0x0000, /* R239 */ + 0x0000, /* R240 */ + 0x0000, /* R241 */ + 0x0000, /* R242 */ + 0x0000, /* R243 */ + 0x0000, /* R244 */ + 0x0000, /* R245 */ + 0x0000, /* R246 */ + 0x0000, /* R247 */ + 0x0000, /* R248 */ + 0x0000, /* R249 */ + 0x0000, /* R250 */ + 0x0000, /* R251 */ + 0x0000, /* R252 */ + 0x0000, /* R253 */ + 0x0000, /* R254 */ + 0x0000, /* R255 */ +}; +#endif + +#ifdef CONFIG_MFD_WM8350_CONFIG_MODE_2 + +#undef WM8350_HAVE_CONFIG_MODE +#define WM8350_HAVE_CONFIG_MODE + +const u16 wm8350_mode2_defaults[] = { + 0x17FF, /* R0 - Reset/ID */ + 0x1000, /* R1 - ID */ + 0x0000, /* R2 */ + 0x1002, /* R3 - System Control 1 */ + 0x0014, /* R4 - System Control 2 */ + 0x0000, /* R5 - System Hibernate */ + 0x8A00, /* R6 - Interface Control */ + 0x0000, /* R7 */ + 0x8000, /* R8 - Power mgmt (1) */ + 0x0000, /* R9 - Power mgmt (2) */ + 0x0000, /* R10 - Power mgmt (3) */ + 0x2000, /* R11 - Power mgmt (4) */ + 0x0E00, /* R12 - Power mgmt (5) */ + 0x0000, /* R13 - Power mgmt (6) */ + 0x0000, /* R14 - Power mgmt (7) */ + 0x0000, /* R15 */ + 0x0000, /* R16 - RTC Seconds/Minutes */ + 0x0100, /* R17 - RTC Hours/Day */ + 0x0101, /* R18 - RTC Date/Month */ + 0x1400, /* R19 - RTC Year */ + 0x0000, /* R20 - Alarm Seconds/Minutes */ + 0x0000, /* R21 - Alarm Hours/Day */ + 0x0000, /* R22 - Alarm Date/Month */ + 0x0320, /* R23 - RTC Time Control */ + 0x0000, /* R24 - System Interrupts */ + 0x0000, /* R25 - Interrupt Status 1 */ + 0x0000, /* R26 - Interrupt Status 2 */ + 0x0000, /* R27 - Power Up Interrupt Status */ + 0x0000, /* R28 - Under Voltage Interrupt status */ + 0x0000, /* R29 - Over Current Interrupt status */ + 0x0000, /* R30 - GPIO Interrupt Status */ + 0x0000, /* R31 - Comparator Interrupt Status */ + 0x3FFF, /* R32 - System Interrupts Mask */ + 0x0000, /* R33 - Interrupt Status 1 Mask */ + 0x0000, /* R34 - Interrupt Status 2 Mask */ + 0x0000, /* R35 - Power Up Interrupt Status Mask */ + 0x0000, /* R36 - Under Voltage Interrupt status Mask */ + 0x0000, /* R37 - Over Current Interrupt status Mask */ + 0x0000, /* R38 - GPIO Interrupt Status Mask */ + 0x0000, /* R39 - Comparator Interrupt Status Mask */ + 0x0040, /* R40 - Clock Control 1 */ + 0x0000, /* R41 - Clock Control 2 */ + 0x3B00, /* R42 - FLL Control 1 */ + 0x7086, /* R43 - FLL Control 2 */ + 0xC226, /* R44 - FLL Control 3 */ + 0x0000, /* R45 - FLL Control 4 */ + 0x0000, /* R46 */ + 0x0000, /* R47 */ + 0x0000, /* R48 - DAC Control */ + 0x0000, /* R49 */ + 0x00C0, /* R50 - DAC Digital Volume L */ + 0x00C0, /* R51 - DAC Digital Volume R */ + 0x0000, /* R52 */ + 0x0040, /* R53 - DAC LR Rate */ + 0x0000, /* R54 - DAC Clock Control */ + 0x0000, /* R55 */ + 0x0000, /* R56 */ + 0x0000, /* R57 */ + 0x4000, /* R58 - DAC Mute */ + 0x0000, /* R59 - DAC Mute Volume */ + 0x0000, /* R60 - DAC Side */ + 0x0000, /* R61 */ + 0x0000, /* R62 */ + 0x0000, /* R63 */ + 0x8000, /* R64 - ADC Control */ + 0x0000, /* R65 */ + 0x00C0, /* R66 - ADC Digital Volume L */ + 0x00C0, /* R67 - ADC Digital Volume R */ + 0x0000, /* R68 - ADC Divider */ + 0x0000, /* R69 */ + 0x0040, /* R70 - ADC LR Rate */ + 0x0000, /* R71 */ + 0x0303, /* R72 - Input Control */ + 0x0000, /* R73 - IN3 Input Control */ + 0x0000, /* R74 - Mic Bias Control */ + 0x0000, /* R75 */ + 0x0000, /* R76 - Output Control */ + 0x0000, /* R77 - Jack Detect */ + 0x0000, /* R78 - Anti Pop Control */ + 0x0000, /* R79 */ + 0x0040, /* R80 - Left Input Volume */ + 0x0040, /* R81 - Right Input Volume */ + 0x0000, /* R82 */ + 0x0000, /* R83 */ + 0x0000, /* R84 */ + 0x0000, /* R85 */ + 0x0000, /* R86 */ + 0x0000, /* R87 */ + 0x0800, /* R88 - Left Mixer Control */ + 0x1000, /* R89 - Right Mixer Control */ + 0x0000, /* R90 */ + 0x0000, /* R91 */ + 0x0000, /* R92 - OUT3 Mixer Control */ + 0x0000, /* R93 - OUT4 Mixer Control */ + 0x0000, /* R94 */ + 0x0000, /* R95 */ + 0x0000, /* R96 - Output Left Mixer Volume */ + 0x0000, /* R97 - Output Right Mixer Volume */ + 0x0000, /* R98 - Input Mixer Volume L */ + 0x0000, /* R99 - Input Mixer Volume R */ + 0x0000, /* R100 - Input Mixer Volume */ + 0x0000, /* R101 */ + 0x0000, /* R102 */ + 0x0000, /* R103 */ + 0x00E4, /* R104 - LOUT1 Volume */ + 0x00E4, /* R105 - ROUT1 Volume */ + 0x00E4, /* R106 - LOUT2 Volume */ + 0x02E4, /* R107 - ROUT2 Volume */ + 0x0000, /* R108 */ + 0x0000, /* R109 */ + 0x0000, /* R110 */ + 0x0000, /* R111 - BEEP Volume */ + 0x0A00, /* R112 - AI Formating */ + 0x0000, /* R113 - ADC DAC COMP */ + 0x0020, /* R114 - AI ADC Control */ + 0x0020, /* R115 - AI DAC Control */ + 0x0000, /* R116 - AIF Test */ + 0x0000, /* R117 */ + 0x0000, /* R118 */ + 0x0000, /* R119 */ + 0x0000, /* R120 */ + 0x0000, /* R121 */ + 0x0000, /* R122 */ + 0x0000, /* R123 */ + 0x0000, /* R124 */ + 0x0000, /* R125 */ + 0x0000, /* R126 */ + 0x0000, /* R127 */ + 0x1FFF, /* R128 - GPIO Debounce */ + 0x0000, /* R129 - GPIO Pin pull up Control */ + 0x03FC, /* R130 - GPIO Pull down Control */ + 0x0000, /* R131 - GPIO Interrupt Mode */ + 0x0000, /* R132 */ + 0x0000, /* R133 - GPIO Control */ + 0x08FB, /* R134 - GPIO Configuration (i/o) */ + 0x0CFE, /* R135 - GPIO Pin Polarity / Type */ + 0x0000, /* R136 */ + 0x0000, /* R137 */ + 0x0000, /* R138 */ + 0x0000, /* R139 */ + 0x0312, /* R140 - GPIO Function Select 1 */ + 0x0003, /* R141 - GPIO Function Select 2 */ + 0x2331, /* R142 - GPIO Function Select 3 */ + 0x0003, /* R143 - GPIO Function Select 4 */ + 0x0000, /* R144 - Digitiser Control (1) */ + 0x0002, /* R145 - Digitiser Control (2) */ + 0x0000, /* R146 */ + 0x0000, /* R147 */ + 0x0000, /* R148 */ + 0x0000, /* R149 */ + 0x0000, /* R150 */ + 0x0000, /* R151 */ + 0x7000, /* R152 - AUX1 Readback */ + 0x7000, /* R153 - AUX2 Readback */ + 0x7000, /* R154 - AUX3 Readback */ + 0x7000, /* R155 - AUX4 Readback */ + 0x0000, /* R156 - USB Voltage Readback */ + 0x0000, /* R157 - LINE Voltage Readback */ + 0x0000, /* R158 - BATT Voltage Readback */ + 0x0000, /* R159 - Chip Temp Readback */ + 0x0000, /* R160 */ + 0x0000, /* R161 */ + 0x0000, /* R162 */ + 0x0000, /* R163 - Generic Comparator Control */ + 0x0000, /* R164 - Generic comparator 1 */ + 0x0000, /* R165 - Generic comparator 2 */ + 0x0000, /* R166 - Generic comparator 3 */ + 0x0000, /* R167 - Generic comparator 4 */ + 0xA00F, /* R168 - Battery Charger Control 1 */ + 0x0B06, /* R169 - Battery Charger Control 2 */ + 0x0000, /* R170 - Battery Charger Control 3 */ + 0x0000, /* R171 */ + 0x0000, /* R172 - Current Sink Driver A */ + 0x0000, /* R173 - CSA Flash control */ + 0x0000, /* R174 - Current Sink Driver B */ + 0x0000, /* R175 - CSB Flash control */ + 0x0000, /* R176 - DCDC/LDO requested */ + 0x002D, /* R177 - DCDC Active options */ + 0x0000, /* R178 - DCDC Sleep options */ + 0x0025, /* R179 - Power-check comparator */ + 0x000E, /* R180 - DCDC1 Control */ + 0x0400, /* R181 - DCDC1 Timeouts */ + 0x1006, /* R182 - DCDC1 Low Power */ + 0x0018, /* R183 - DCDC2 Control */ + 0x0000, /* R184 - DCDC2 Timeouts */ + 0x0000, /* R185 */ + 0x002E, /* R186 - DCDC3 Control */ + 0x0800, /* R187 - DCDC3 Timeouts */ + 0x0006, /* R188 - DCDC3 Low Power */ + 0x000E, /* R189 - DCDC4 Control */ + 0x0800, /* R190 - DCDC4 Timeouts */ + 0x0006, /* R191 - DCDC4 Low Power */ + 0x0008, /* R192 - DCDC5 Control */ + 0x0000, /* R193 - DCDC5 Timeouts */ + 0x0000, /* R194 */ + 0x0026, /* R195 - DCDC6 Control */ + 0x0C00, /* R196 - DCDC6 Timeouts */ + 0x0006, /* R197 - DCDC6 Low Power */ + 0x0000, /* R198 */ + 0x0003, /* R199 - Limit Switch Control */ + 0x001A, /* R200 - LDO1 Control */ + 0x0800, /* R201 - LDO1 Timeouts */ + 0x001C, /* R202 - LDO1 Low Power */ + 0x0010, /* R203 - LDO2 Control */ + 0x0800, /* R204 - LDO2 Timeouts */ + 0x001C, /* R205 - LDO2 Low Power */ + 0x000A, /* R206 - LDO3 Control */ + 0x0C00, /* R207 - LDO3 Timeouts */ + 0x001C, /* R208 - LDO3 Low Power */ + 0x001A, /* R209 - LDO4 Control */ + 0x0800, /* R210 - LDO4 Timeouts */ + 0x001C, /* R211 - LDO4 Low Power */ + 0x0000, /* R212 */ + 0x0000, /* R213 */ + 0x0000, /* R214 */ + 0x0000, /* R215 - VCC_FAULT Masks */ + 0x001F, /* R216 - Main Bandgap Control */ + 0x0000, /* R217 - OSC Control */ + 0x9000, /* R218 - RTC Tick Control */ + 0x0000, /* R219 */ + 0x4000, /* R220 - RAM BIST 1 */ + 0x0000, /* R221 */ + 0x0000, /* R222 */ + 0x0000, /* R223 */ + 0x0000, /* R224 */ + 0x0000, /* R225 - DCDC/LDO status */ + 0x0000, /* R226 */ + 0x0000, /* R227 */ + 0x0000, /* R228 */ + 0x0000, /* R229 */ + 0xE000, /* R230 - GPIO Pin Status */ + 0x0000, /* R231 */ + 0x0000, /* R232 */ + 0x0000, /* R233 */ + 0x0000, /* R234 */ + 0x0000, /* R235 */ + 0x0000, /* R236 */ + 0x0000, /* R237 */ + 0x0000, /* R238 */ + 0x0000, /* R239 */ + 0x0000, /* R240 */ + 0x0000, /* R241 */ + 0x0000, /* R242 */ + 0x0000, /* R243 */ + 0x0000, /* R244 */ + 0x0000, /* R245 */ + 0x0000, /* R246 */ + 0x0000, /* R247 */ + 0x0000, /* R248 */ + 0x0000, /* R249 */ + 0x0000, /* R250 */ + 0x0000, /* R251 */ + 0x0000, /* R252 */ + 0x0000, /* R253 */ + 0x0000, /* R254 */ + 0x0000, /* R255 */ +}; +#endif + +#ifdef CONFIG_MFD_WM8350_CONFIG_MODE_3 + +#undef WM8350_HAVE_CONFIG_MODE +#define WM8350_HAVE_CONFIG_MODE + +const u16 wm8350_mode3_defaults[] = { + 0x17FF, /* R0 - Reset/ID */ + 0x1000, /* R1 - ID */ + 0x0000, /* R2 */ + 0x1000, /* R3 - System Control 1 */ + 0x0004, /* R4 - System Control 2 */ + 0x0000, /* R5 - System Hibernate */ + 0x8A00, /* R6 - Interface Control */ + 0x0000, /* R7 */ + 0x8000, /* R8 - Power mgmt (1) */ + 0x0000, /* R9 - Power mgmt (2) */ + 0x0000, /* R10 - Power mgmt (3) */ + 0x2000, /* R11 - Power mgmt (4) */ + 0x0E00, /* R12 - Power mgmt (5) */ + 0x0000, /* R13 - Power mgmt (6) */ + 0x0000, /* R14 - Power mgmt (7) */ + 0x0000, /* R15 */ + 0x0000, /* R16 - RTC Seconds/Minutes */ + 0x0100, /* R17 - RTC Hours/Day */ + 0x0101, /* R18 - RTC Date/Month */ + 0x1400, /* R19 - RTC Year */ + 0x0000, /* R20 - Alarm Seconds/Minutes */ + 0x0000, /* R21 - Alarm Hours/Day */ + 0x0000, /* R22 - Alarm Date/Month */ + 0x0320, /* R23 - RTC Time Control */ + 0x0000, /* R24 - System Interrupts */ + 0x0000, /* R25 - Interrupt Status 1 */ + 0x0000, /* R26 - Interrupt Status 2 */ + 0x0000, /* R27 - Power Up Interrupt Status */ + 0x0000, /* R28 - Under Voltage Interrupt status */ + 0x0000, /* R29 - Over Current Interrupt status */ + 0x0000, /* R30 - GPIO Interrupt Status */ + 0x0000, /* R31 - Comparator Interrupt Status */ + 0x3FFF, /* R32 - System Interrupts Mask */ + 0x0000, /* R33 - Interrupt Status 1 Mask */ + 0x0000, /* R34 - Interrupt Status 2 Mask */ + 0x0000, /* R35 - Power Up Interrupt Status Mask */ + 0x0000, /* R36 - Under Voltage Interrupt status Mask */ + 0x0000, /* R37 - Over Current Interrupt status Mask */ + 0x0000, /* R38 - GPIO Interrupt Status Mask */ + 0x0000, /* R39 - Comparator Interrupt Status Mask */ + 0x0040, /* R40 - Clock Control 1 */ + 0x0000, /* R41 - Clock Control 2 */ + 0x3B00, /* R42 - FLL Control 1 */ + 0x7086, /* R43 - FLL Control 2 */ + 0xC226, /* R44 - FLL Control 3 */ + 0x0000, /* R45 - FLL Control 4 */ + 0x0000, /* R46 */ + 0x0000, /* R47 */ + 0x0000, /* R48 - DAC Control */ + 0x0000, /* R49 */ + 0x00C0, /* R50 - DAC Digital Volume L */ + 0x00C0, /* R51 - DAC Digital Volume R */ + 0x0000, /* R52 */ + 0x0040, /* R53 - DAC LR Rate */ + 0x0000, /* R54 - DAC Clock Control */ + 0x0000, /* R55 */ + 0x0000, /* R56 */ + 0x0000, /* R57 */ + 0x4000, /* R58 - DAC Mute */ + 0x0000, /* R59 - DAC Mute Volume */ + 0x0000, /* R60 - DAC Side */ + 0x0000, /* R61 */ + 0x0000, /* R62 */ + 0x0000, /* R63 */ + 0x8000, /* R64 - ADC Control */ + 0x0000, /* R65 */ + 0x00C0, /* R66 - ADC Digital Volume L */ + 0x00C0, /* R67 - ADC Digital Volume R */ + 0x0000, /* R68 - ADC Divider */ + 0x0000, /* R69 */ + 0x0040, /* R70 - ADC LR Rate */ + 0x0000, /* R71 */ + 0x0303, /* R72 - Input Control */ + 0x0000, /* R73 - IN3 Input Control */ + 0x0000, /* R74 - Mic Bias Control */ + 0x0000, /* R75 */ + 0x0000, /* R76 - Output Control */ + 0x0000, /* R77 - Jack Detect */ + 0x0000, /* R78 - Anti Pop Control */ + 0x0000, /* R79 */ + 0x0040, /* R80 - Left Input Volume */ + 0x0040, /* R81 - Right Input Volume */ + 0x0000, /* R82 */ + 0x0000, /* R83 */ + 0x0000, /* R84 */ + 0x0000, /* R85 */ + 0x0000, /* R86 */ + 0x0000, /* R87 */ + 0x0800, /* R88 - Left Mixer Control */ + 0x1000, /* R89 - Right Mixer Control */ + 0x0000, /* R90 */ + 0x0000, /* R91 */ + 0x0000, /* R92 - OUT3 Mixer Control */ + 0x0000, /* R93 - OUT4 Mixer Control */ + 0x0000, /* R94 */ + 0x0000, /* R95 */ + 0x0000, /* R96 - Output Left Mixer Volume */ + 0x0000, /* R97 - Output Right Mixer Volume */ + 0x0000, /* R98 - Input Mixer Volume L */ + 0x0000, /* R99 - Input Mixer Volume R */ + 0x0000, /* R100 - Input Mixer Volume */ + 0x0000, /* R101 */ + 0x0000, /* R102 */ + 0x0000, /* R103 */ + 0x00E4, /* R104 - LOUT1 Volume */ + 0x00E4, /* R105 - ROUT1 Volume */ + 0x00E4, /* R106 - LOUT2 Volume */ + 0x02E4, /* R107 - ROUT2 Volume */ + 0x0000, /* R108 */ + 0x0000, /* R109 */ + 0x0000, /* R110 */ + 0x0000, /* R111 - BEEP Volume */ + 0x0A00, /* R112 - AI Formating */ + 0x0000, /* R113 - ADC DAC COMP */ + 0x0020, /* R114 - AI ADC Control */ + 0x0020, /* R115 - AI DAC Control */ + 0x0000, /* R116 - AIF Test */ + 0x0000, /* R117 */ + 0x0000, /* R118 */ + 0x0000, /* R119 */ + 0x0000, /* R120 */ + 0x0000, /* R121 */ + 0x0000, /* R122 */ + 0x0000, /* R123 */ + 0x0000, /* R124 */ + 0x0000, /* R125 */ + 0x0000, /* R126 */ + 0x0000, /* R127 */ + 0x1FFF, /* R128 - GPIO Debounce */ + 0x0000, /* R129 - GPIO Pin pull up Control */ + 0x03FC, /* R130 - GPIO Pull down Control */ + 0x0000, /* R131 - GPIO Interrupt Mode */ + 0x0000, /* R132 */ + 0x0000, /* R133 - GPIO Control */ + 0x0A7B, /* R134 - GPIO Configuration (i/o) */ + 0x06FE, /* R135 - GPIO Pin Polarity / Type */ + 0x0000, /* R136 */ + 0x0000, /* R137 */ + 0x0000, /* R138 */ + 0x0000, /* R139 */ + 0x1312, /* R140 - GPIO Function Select 1 */ + 0x1030, /* R141 - GPIO Function Select 2 */ + 0x2231, /* R142 - GPIO Function Select 3 */ + 0x0003, /* R143 - GPIO Function Select 4 */ + 0x0000, /* R144 - Digitiser Control (1) */ + 0x0002, /* R145 - Digitiser Control (2) */ + 0x0000, /* R146 */ + 0x0000, /* R147 */ + 0x0000, /* R148 */ + 0x0000, /* R149 */ + 0x0000, /* R150 */ + 0x0000, /* R151 */ + 0x7000, /* R152 - AUX1 Readback */ + 0x7000, /* R153 - AUX2 Readback */ + 0x7000, /* R154 - AUX3 Readback */ + 0x7000, /* R155 - AUX4 Readback */ + 0x0000, /* R156 - USB Voltage Readback */ + 0x0000, /* R157 - LINE Voltage Readback */ + 0x0000, /* R158 - BATT Voltage Readback */ + 0x0000, /* R159 - Chip Temp Readback */ + 0x0000, /* R160 */ + 0x0000, /* R161 */ + 0x0000, /* R162 */ + 0x0000, /* R163 - Generic Comparator Control */ + 0x0000, /* R164 - Generic comparator 1 */ + 0x0000, /* R165 - Generic comparator 2 */ + 0x0000, /* R166 - Generic comparator 3 */ + 0x0000, /* R167 - Generic comparator 4 */ + 0xA00F, /* R168 - Battery Charger Control 1 */ + 0x0B06, /* R169 - Battery Charger Control 2 */ + 0x0000, /* R170 - Battery Charger Control 3 */ + 0x0000, /* R171 */ + 0x0000, /* R172 - Current Sink Driver A */ + 0x0000, /* R173 - CSA Flash control */ + 0x0000, /* R174 - Current Sink Driver B */ + 0x0000, /* R175 - CSB Flash control */ + 0x0000, /* R176 - DCDC/LDO requested */ + 0x002D, /* R177 - DCDC Active options */ + 0x0000, /* R178 - DCDC Sleep options */ + 0x0025, /* R179 - Power-check comparator */ + 0x000E, /* R180 - DCDC1 Control */ + 0x0400, /* R181 - DCDC1 Timeouts */ + 0x1006, /* R182 - DCDC1 Low Power */ + 0x0018, /* R183 - DCDC2 Control */ + 0x0000, /* R184 - DCDC2 Timeouts */ + 0x0000, /* R185 */ + 0x000E, /* R186 - DCDC3 Control */ + 0x0400, /* R187 - DCDC3 Timeouts */ + 0x0006, /* R188 - DCDC3 Low Power */ + 0x0026, /* R189 - DCDC4 Control */ + 0x0400, /* R190 - DCDC4 Timeouts */ + 0x0006, /* R191 - DCDC4 Low Power */ + 0x0008, /* R192 - DCDC5 Control */ + 0x0000, /* R193 - DCDC5 Timeouts */ + 0x0000, /* R194 */ + 0x0026, /* R195 - DCDC6 Control */ + 0x0400, /* R196 - DCDC6 Timeouts */ + 0x0006, /* R197 - DCDC6 Low Power */ + 0x0000, /* R198 */ + 0x0003, /* R199 - Limit Switch Control */ + 0x001C, /* R200 - LDO1 Control */ + 0x0000, /* R201 - LDO1 Timeouts */ + 0x001C, /* R202 - LDO1 Low Power */ + 0x001C, /* R203 - LDO2 Control */ + 0x0400, /* R204 - LDO2 Timeouts */ + 0x001C, /* R205 - LDO2 Low Power */ + 0x001C, /* R206 - LDO3 Control */ + 0x0400, /* R207 - LDO3 Timeouts */ + 0x001C, /* R208 - LDO3 Low Power */ + 0x001F, /* R209 - LDO4 Control */ + 0x0400, /* R210 - LDO4 Timeouts */ + 0x001C, /* R211 - LDO4 Low Power */ + 0x0000, /* R212 */ + 0x0000, /* R213 */ + 0x0000, /* R214 */ + 0x0000, /* R215 - VCC_FAULT Masks */ + 0x001F, /* R216 - Main Bandgap Control */ + 0x0000, /* R217 - OSC Control */ + 0x9000, /* R218 - RTC Tick Control */ + 0x0000, /* R219 */ + 0x4000, /* R220 - RAM BIST 1 */ + 0x0000, /* R221 */ + 0x0000, /* R222 */ + 0x0000, /* R223 */ + 0x0000, /* R224 */ + 0x0000, /* R225 - DCDC/LDO status */ + 0x0000, /* R226 */ + 0x0000, /* R227 */ + 0x0000, /* R228 */ + 0x0000, /* R229 */ + 0xE000, /* R230 - GPIO Pin Status */ + 0x0000, /* R231 */ + 0x0000, /* R232 */ + 0x0000, /* R233 */ + 0x0000, /* R234 */ + 0x0000, /* R235 */ + 0x0000, /* R236 */ + 0x0000, /* R237 */ + 0x0000, /* R238 */ + 0x0000, /* R239 */ + 0x0000, /* R240 */ + 0x0000, /* R241 */ + 0x0000, /* R242 */ + 0x0000, /* R243 */ + 0x0000, /* R244 */ + 0x0000, /* R245 */ + 0x0000, /* R246 */ + 0x0000, /* R247 */ + 0x0000, /* R248 */ + 0x0000, /* R249 */ + 0x0000, /* R250 */ + 0x0000, /* R251 */ + 0x0000, /* R252 */ + 0x0000, /* R253 */ + 0x0000, /* R254 */ + 0x0000, /* R255 */ +}; +#endif + +/* The register defaults for the config mode used must be compiled in but + * due to the impact on kernel size it is possible to disable + */ +#ifndef WM8350_HAVE_CONFIG_MODE +#warning No WM8350 config modes supported - select at least one of the +#warning MFD_WM8350_CONFIG_MODE_n options from the board driver. +#endif + +/* + * Access masks. + */ + +const struct wm8350_reg_access wm8350_reg_io_map[] = { + /* read write volatile */ + { 0xFFFF, 0xFFFF, 0xFFFF }, /* R0 - Reset/ID */ + { 0x7CFF, 0x0C00, 0x7FFF }, /* R1 - ID */ + { 0x0000, 0x0000, 0x0000 }, /* R2 */ + { 0xBE3B, 0xBE3B, 0x8000 }, /* R3 - System Control 1 */ + { 0xFCF7, 0xFCF7, 0xF800 }, /* R4 - System Control 2 */ + { 0x80FF, 0x80FF, 0x8000 }, /* R5 - System Hibernate */ + { 0xFB0E, 0xFB0E, 0x0000 }, /* R6 - Interface Control */ + { 0x0000, 0x0000, 0x0000 }, /* R7 */ + { 0xE537, 0xE537, 0xFFFF }, /* R8 - Power mgmt (1) */ + { 0x0FF3, 0x0FF3, 0xFFFF }, /* R9 - Power mgmt (2) */ + { 0x008F, 0x008F, 0xFFFF }, /* R10 - Power mgmt (3) */ + { 0x6D3C, 0x6D3C, 0xFFFF }, /* R11 - Power mgmt (4) */ + { 0x1F8F, 0x1F8F, 0xFFFF }, /* R12 - Power mgmt (5) */ + { 0x8F3F, 0x8F3F, 0xFFFF }, /* R13 - Power mgmt (6) */ + { 0x0003, 0x0003, 0xFFFF }, /* R14 - Power mgmt (7) */ + { 0x0000, 0x0000, 0x0000 }, /* R15 */ + { 0x7F7F, 0x7F7F, 0xFFFF }, /* R16 - RTC Seconds/Minutes */ + { 0x073F, 0x073F, 0xFFFF }, /* R17 - RTC Hours/Day */ + { 0x1F3F, 0x1F3F, 0xFFFF }, /* R18 - RTC Date/Month */ + { 0x3FFF, 0x00FF, 0xFFFF }, /* R19 - RTC Year */ + { 0x7F7F, 0x7F7F, 0x0000 }, /* R20 - Alarm Seconds/Minutes */ + { 0x0F3F, 0x0F3F, 0x0000 }, /* R21 - Alarm Hours/Day */ + { 0x1F3F, 0x1F3F, 0x0000 }, /* R22 - Alarm Date/Month */ + { 0xEF7F, 0xEA7F, 0xFFFF }, /* R23 - RTC Time Control */ + { 0x3BFF, 0x0000, 0xFFFF }, /* R24 - System Interrupts */ + { 0xFEE7, 0x0000, 0xFFFF }, /* R25 - Interrupt Status 1 */ + { 0x35FF, 0x0000, 0xFFFF }, /* R26 - Interrupt Status 2 */ + { 0x0F3F, 0x0000, 0xFFFF }, /* R27 - Power Up Interrupt Status */ + { 0x0F3F, 0x0000, 0xFFFF }, /* R28 - Under Voltage Interrupt status */ + { 0x8000, 0x0000, 0xFFFF }, /* R29 - Over Current Interrupt status */ + { 0x1FFF, 0x0000, 0xFFFF }, /* R30 - GPIO Interrupt Status */ + { 0xEF7F, 0x0000, 0xFFFF }, /* R31 - Comparator Interrupt Status */ + { 0x3FFF, 0x3FFF, 0x0000 }, /* R32 - System Interrupts Mask */ + { 0xFEE7, 0xFEE7, 0x0000 }, /* R33 - Interrupt Status 1 Mask */ + { 0xF5FF, 0xF5FF, 0x0000 }, /* R34 - Interrupt Status 2 Mask */ + { 0x0F3F, 0x0F3F, 0x0000 }, /* R35 - Power Up Interrupt Status Mask */ + { 0x0F3F, 0x0F3F, 0x0000 }, /* R36 - Under Voltage Int status Mask */ + { 0x8000, 0x8000, 0x0000 }, /* R37 - Over Current Int status Mask */ + { 0x1FFF, 0x1FFF, 0x0000 }, /* R38 - GPIO Interrupt Status Mask */ + { 0xEF7F, 0xEF7F, 0x0000 }, /* R39 - Comparator IntStatus Mask */ + { 0xC9F7, 0xC9F7, 0xFFFF }, /* R40 - Clock Control 1 */ + { 0x8001, 0x8001, 0x0000 }, /* R41 - Clock Control 2 */ + { 0xFFF7, 0xFFF7, 0xFFFF }, /* R42 - FLL Control 1 */ + { 0xFBFF, 0xFBFF, 0x0000 }, /* R43 - FLL Control 2 */ + { 0xFFFF, 0xFFFF, 0x0000 }, /* R44 - FLL Control 3 */ + { 0x0033, 0x0033, 0x0000 }, /* R45 - FLL Control 4 */ + { 0x0000, 0x0000, 0x0000 }, /* R46 */ + { 0x0000, 0x0000, 0x0000 }, /* R47 */ + { 0x3033, 0x3033, 0x0000 }, /* R48 - DAC Control */ + { 0x0000, 0x0000, 0x0000 }, /* R49 */ + { 0x81FF, 0x81FF, 0xFFFF }, /* R50 - DAC Digital Volume L */ + { 0x81FF, 0x81FF, 0xFFFF }, /* R51 - DAC Digital Volume R */ + { 0x0000, 0x0000, 0x0000 }, /* R52 */ + { 0x0FFF, 0x0FFF, 0xFFFF }, /* R53 - DAC LR Rate */ + { 0x0017, 0x0017, 0x0000 }, /* R54 - DAC Clock Control */ + { 0x0000, 0x0000, 0x0000 }, /* R55 */ + { 0x0000, 0x0000, 0x0000 }, /* R56 */ + { 0x0000, 0x0000, 0x0000 }, /* R57 */ + { 0x4000, 0x4000, 0x0000 }, /* R58 - DAC Mute */ + { 0x7000, 0x7000, 0x0000 }, /* R59 - DAC Mute Volume */ + { 0x3C00, 0x3C00, 0x0000 }, /* R60 - DAC Side */ + { 0x0000, 0x0000, 0x0000 }, /* R61 */ + { 0x0000, 0x0000, 0x0000 }, /* R62 */ + { 0x0000, 0x0000, 0x0000 }, /* R63 */ + { 0x8303, 0x8303, 0xFFFF }, /* R64 - ADC Control */ + { 0x0000, 0x0000, 0x0000 }, /* R65 */ + { 0x81FF, 0x81FF, 0xFFFF }, /* R66 - ADC Digital Volume L */ + { 0x81FF, 0x81FF, 0xFFFF }, /* R67 - ADC Digital Volume R */ + { 0x0FFF, 0x0FFF, 0x0000 }, /* R68 - ADC Divider */ + { 0x0000, 0x0000, 0x0000 }, /* R69 */ + { 0x0FFF, 0x0FFF, 0xFFFF }, /* R70 - ADC LR Rate */ + { 0x0000, 0x0000, 0x0000 }, /* R71 */ + { 0x0707, 0x0707, 0xFFFF }, /* R72 - Input Control */ + { 0xC0C0, 0xC0C0, 0xFFFF }, /* R73 - IN3 Input Control */ + { 0xC09F, 0xC09F, 0xFFFF }, /* R74 - Mic Bias Control */ + { 0x0000, 0x0000, 0x0000 }, /* R75 */ + { 0x0F15, 0x0F15, 0xFFFF }, /* R76 - Output Control */ + { 0xC000, 0xC000, 0xFFFF }, /* R77 - Jack Detect */ + { 0x03FF, 0x03FF, 0x0000 }, /* R78 - Anti Pop Control */ + { 0x0000, 0x0000, 0x0000 }, /* R79 */ + { 0xE1FC, 0xE1FC, 0x8000 }, /* R80 - Left Input Volume */ + { 0xE1FC, 0xE1FC, 0x8000 }, /* R81 - Right Input Volume */ + { 0x0000, 0x0000, 0x0000 }, /* R82 */ + { 0x0000, 0x0000, 0x0000 }, /* R83 */ + { 0x0000, 0x0000, 0x0000 }, /* R84 */ + { 0x0000, 0x0000, 0x0000 }, /* R85 */ + { 0x0000, 0x0000, 0x0000 }, /* R86 */ + { 0x0000, 0x0000, 0x0000 }, /* R87 */ + { 0x9807, 0x9807, 0xFFFF }, /* R88 - Left Mixer Control */ + { 0x980B, 0x980B, 0xFFFF }, /* R89 - Right Mixer Control */ + { 0x0000, 0x0000, 0x0000 }, /* R90 */ + { 0x0000, 0x0000, 0x0000 }, /* R91 */ + { 0x8909, 0x8909, 0xFFFF }, /* R92 - OUT3 Mixer Control */ + { 0x9E07, 0x9E07, 0xFFFF }, /* R93 - OUT4 Mixer Control */ + { 0x0000, 0x0000, 0x0000 }, /* R94 */ + { 0x0000, 0x0000, 0x0000 }, /* R95 */ + { 0x0EEE, 0x0EEE, 0x0000 }, /* R96 - Output Left Mixer Volume */ + { 0xE0EE, 0xE0EE, 0x0000 }, /* R97 - Output Right Mixer Volume */ + { 0x0E0F, 0x0E0F, 0x0000 }, /* R98 - Input Mixer Volume L */ + { 0xE0E1, 0xE0E1, 0x0000 }, /* R99 - Input Mixer Volume R */ + { 0x800E, 0x800E, 0x0000 }, /* R100 - Input Mixer Volume */ + { 0x0000, 0x0000, 0x0000 }, /* R101 */ + { 0x0000, 0x0000, 0x0000 }, /* R102 */ + { 0x0000, 0x0000, 0x0000 }, /* R103 */ + { 0xE1FC, 0xE1FC, 0xFFFF }, /* R104 - LOUT1 Volume */ + { 0xE1FC, 0xE1FC, 0xFFFF }, /* R105 - ROUT1 Volume */ + { 0xE1FC, 0xE1FC, 0xFFFF }, /* R106 - LOUT2 Volume */ + { 0xE7FC, 0xE7FC, 0xFFFF }, /* R107 - ROUT2 Volume */ + { 0x0000, 0x0000, 0x0000 }, /* R108 */ + { 0x0000, 0x0000, 0x0000 }, /* R109 */ + { 0x0000, 0x0000, 0x0000 }, /* R110 */ + { 0x80E0, 0x80E0, 0xFFFF }, /* R111 - BEEP Volume */ + { 0xBF00, 0xBF00, 0x0000 }, /* R112 - AI Formating */ + { 0x00F1, 0x00F1, 0x0000 }, /* R113 - ADC DAC COMP */ + { 0x00F8, 0x00F8, 0x0000 }, /* R114 - AI ADC Control */ + { 0x40FB, 0x40FB, 0x0000 }, /* R115 - AI DAC Control */ + { 0x7C30, 0x7C30, 0x0000 }, /* R116 - AIF Test */ + { 0x0000, 0x0000, 0x0000 }, /* R117 */ + { 0x0000, 0x0000, 0x0000 }, /* R118 */ + { 0x0000, 0x0000, 0x0000 }, /* R119 */ + { 0x0000, 0x0000, 0x0000 }, /* R120 */ + { 0x0000, 0x0000, 0x0000 }, /* R121 */ + { 0x0000, 0x0000, 0x0000 }, /* R122 */ + { 0x0000, 0x0000, 0x0000 }, /* R123 */ + { 0x0000, 0x0000, 0x0000 }, /* R124 */ + { 0x0000, 0x0000, 0x0000 }, /* R125 */ + { 0x0000, 0x0000, 0x0000 }, /* R126 */ + { 0x0000, 0x0000, 0x0000 }, /* R127 */ + { 0x1FFF, 0x1FFF, 0x0000 }, /* R128 - GPIO Debounce */ + { 0x1FFF, 0x1FFF, 0x0000 }, /* R129 - GPIO Pin pull up Control */ + { 0x1FFF, 0x1FFF, 0x0000 }, /* R130 - GPIO Pull down Control */ + { 0x1FFF, 0x1FFF, 0x0000 }, /* R131 - GPIO Interrupt Mode */ + { 0x0000, 0x0000, 0x0000 }, /* R132 */ + { 0x00C0, 0x00C0, 0x0000 }, /* R133 - GPIO Control */ + { 0x1FFF, 0x1FFF, 0x0000 }, /* R134 - GPIO Configuration (i/o) */ + { 0x1FFF, 0x1FFF, 0x0000 }, /* R135 - GPIO Pin Polarity / Type */ + { 0x0000, 0x0000, 0x0000 }, /* R136 */ + { 0x0000, 0x0000, 0x0000 }, /* R137 */ + { 0x0000, 0x0000, 0x0000 }, /* R138 */ + { 0x0000, 0x0000, 0x0000 }, /* R139 */ + { 0xFFFF, 0xFFFF, 0x0000 }, /* R140 - GPIO Function Select 1 */ + { 0xFFFF, 0xFFFF, 0x0000 }, /* R141 - GPIO Function Select 2 */ + { 0xFFFF, 0xFFFF, 0x0000 }, /* R142 - GPIO Function Select 3 */ + { 0x000F, 0x000F, 0x0000 }, /* R143 - GPIO Function Select 4 */ + { 0xF0FF, 0xF0FF, 0xA000 }, /* R144 - Digitiser Control (1) */ + { 0x3707, 0x3707, 0x0000 }, /* R145 - Digitiser Control (2) */ + { 0x0000, 0x0000, 0x0000 }, /* R146 */ + { 0x0000, 0x0000, 0x0000 }, /* R147 */ + { 0x0000, 0x0000, 0x0000 }, /* R148 */ + { 0x0000, 0x0000, 0x0000 }, /* R149 */ + { 0x0000, 0x0000, 0x0000 }, /* R150 */ + { 0x0000, 0x0000, 0x0000 }, /* R151 */ + { 0x7FFF, 0x7000, 0xFFFF }, /* R152 - AUX1 Readback */ + { 0x7FFF, 0x7000, 0xFFFF }, /* R153 - AUX2 Readback */ + { 0x7FFF, 0x7000, 0xFFFF }, /* R154 - AUX3 Readback */ + { 0x7FFF, 0x7000, 0xFFFF }, /* R155 - AUX4 Readback */ + { 0x0FFF, 0x0000, 0xFFFF }, /* R156 - USB Voltage Readback */ + { 0x0FFF, 0x0000, 0xFFFF }, /* R157 - LINE Voltage Readback */ + { 0x0FFF, 0x0000, 0xFFFF }, /* R158 - BATT Voltage Readback */ + { 0x0FFF, 0x0000, 0xFFFF }, /* R159 - Chip Temp Readback */ + { 0x0000, 0x0000, 0x0000 }, /* R160 */ + { 0x0000, 0x0000, 0x0000 }, /* R161 */ + { 0x0000, 0x0000, 0x0000 }, /* R162 */ + { 0x000F, 0x000F, 0x0000 }, /* R163 - Generic Comparator Control */ + { 0xFFFF, 0xFFFF, 0x0000 }, /* R164 - Generic comparator 1 */ + { 0xFFFF, 0xFFFF, 0x0000 }, /* R165 - Generic comparator 2 */ + { 0xFFFF, 0xFFFF, 0x0000 }, /* R166 - Generic comparator 3 */ + { 0xFFFF, 0xFFFF, 0x0000 }, /* R167 - Generic comparator 4 */ + { 0xBFFF, 0xBFFF, 0x8000 }, /* R168 - Battery Charger Control 1 */ + { 0xFFFF, 0x4FFF, 0xB000 }, /* R169 - Battery Charger Control 2 */ + { 0x007F, 0x007F, 0x0000 }, /* R170 - Battery Charger Control 3 */ + { 0x0000, 0x0000, 0x0000 }, /* R171 */ + { 0x903F, 0x903F, 0xFFFF }, /* R172 - Current Sink Driver A */ + { 0xE333, 0xE333, 0xFFFF }, /* R173 - CSA Flash control */ + { 0x903F, 0x903F, 0xFFFF }, /* R174 - Current Sink Driver B */ + { 0xE333, 0xE333, 0xFFFF }, /* R175 - CSB Flash control */ + { 0x8F3F, 0x8F3F, 0xFFFF }, /* R176 - DCDC/LDO requested */ + { 0x332D, 0x332D, 0x0000 }, /* R177 - DCDC Active options */ + { 0x002D, 0x002D, 0x0000 }, /* R178 - DCDC Sleep options */ + { 0x5177, 0x5177, 0x8000 }, /* R179 - Power-check comparator */ + { 0x047F, 0x047F, 0x0000 }, /* R180 - DCDC1 Control */ + { 0xFFC0, 0xFFC0, 0x0000 }, /* R181 - DCDC1 Timeouts */ + { 0x737F, 0x737F, 0x0000 }, /* R182 - DCDC1 Low Power */ + { 0x535B, 0x535B, 0x0000 }, /* R183 - DCDC2 Control */ + { 0xFFC0, 0xFFC0, 0x0000 }, /* R184 - DCDC2 Timeouts */ + { 0x0000, 0x0000, 0x0000 }, /* R185 */ + { 0x047F, 0x047F, 0x0000 }, /* R186 - DCDC3 Control */ + { 0xFFC0, 0xFFC0, 0x0000 }, /* R187 - DCDC3 Timeouts */ + { 0x737F, 0x737F, 0x0000 }, /* R188 - DCDC3 Low Power */ + { 0x047F, 0x047F, 0x0000 }, /* R189 - DCDC4 Control */ + { 0xFFC0, 0xFFC0, 0x0000 }, /* R190 - DCDC4 Timeouts */ + { 0x737F, 0x737F, 0x0000 }, /* R191 - DCDC4 Low Power */ + { 0x535B, 0x535B, 0x0000 }, /* R192 - DCDC5 Control */ + { 0xFFC0, 0xFFC0, 0x0000 }, /* R193 - DCDC5 Timeouts */ + { 0x0000, 0x0000, 0x0000 }, /* R194 */ + { 0x047F, 0x047F, 0x0000 }, /* R195 - DCDC6 Control */ + { 0xFFC0, 0xFFC0, 0x0000 }, /* R196 - DCDC6 Timeouts */ + { 0x737F, 0x737F, 0x0000 }, /* R197 - DCDC6 Low Power */ + { 0x0000, 0x0000, 0x0000 }, /* R198 */ + { 0xFFD3, 0xFFD3, 0x0000 }, /* R199 - Limit Switch Control */ + { 0x441F, 0x441F, 0x0000 }, /* R200 - LDO1 Control */ + { 0xFFC0, 0xFFC0, 0x0000 }, /* R201 - LDO1 Timeouts */ + { 0x331F, 0x331F, 0x0000 }, /* R202 - LDO1 Low Power */ + { 0x441F, 0x441F, 0x0000 }, /* R203 - LDO2 Control */ + { 0xFFC0, 0xFFC0, 0x0000 }, /* R204 - LDO2 Timeouts */ + { 0x331F, 0x331F, 0x0000 }, /* R205 - LDO2 Low Power */ + { 0x441F, 0x441F, 0x0000 }, /* R206 - LDO3 Control */ + { 0xFFC0, 0xFFC0, 0x0000 }, /* R207 - LDO3 Timeouts */ + { 0x331F, 0x331F, 0x0000 }, /* R208 - LDO3 Low Power */ + { 0x441F, 0x441F, 0x0000 }, /* R209 - LDO4 Control */ + { 0xFFC0, 0xFFC0, 0x0000 }, /* R210 - LDO4 Timeouts */ + { 0x331F, 0x331F, 0x0000 }, /* R211 - LDO4 Low Power */ + { 0x0000, 0x0000, 0x0000 }, /* R212 */ + { 0x0000, 0x0000, 0x0000 }, /* R213 */ + { 0x0000, 0x0000, 0x0000 }, /* R214 */ + { 0x8F3F, 0x8F3F, 0x0000 }, /* R215 - VCC_FAULT Masks */ + { 0xFF3F, 0xE03F, 0x0000 }, /* R216 - Main Bandgap Control */ + { 0xEF2F, 0xE02F, 0x0000 }, /* R217 - OSC Control */ + { 0xF3FF, 0xB3FF, 0xc000 }, /* R218 - RTC Tick Control */ + { 0xFFFF, 0xFFFF, 0xFFFF }, /* R219 */ + { 0x09FF, 0x01FF, 0x0000 }, /* R220 - RAM BIST 1 */ + { 0x0000, 0x0000, 0x0000 }, /* R221 */ + { 0xFFFF, 0xFFFF, 0xFFFF }, /* R222 */ + { 0xFFFF, 0xFFFF, 0xFFFF }, /* R223 */ + { 0x0000, 0x0000, 0x0000 }, /* R224 */ + { 0x8F3F, 0x0000, 0xFFFF }, /* R225 - DCDC/LDO status */ + { 0x0000, 0x0000, 0x0000 }, /* R226 */ + { 0x0000, 0x0000, 0xFFFF }, /* R227 */ + { 0x0000, 0x0000, 0x0000 }, /* R228 */ + { 0x0000, 0x0000, 0x0000 }, /* R229 */ + { 0xFFFF, 0x1FFF, 0xFFFF }, /* R230 - GPIO Pin Status */ + { 0xFFFF, 0x1FFF, 0xFFFF }, /* R231 */ + { 0xFFFF, 0x1FFF, 0xFFFF }, /* R232 */ + { 0xFFFF, 0x1FFF, 0xFFFF }, /* R233 */ + { 0x0000, 0x0000, 0x0000 }, /* R234 */ + { 0x0000, 0x0000, 0x0000 }, /* R235 */ + { 0x0000, 0x0000, 0x0000 }, /* R236 */ + { 0x0000, 0x0000, 0x0000 }, /* R237 */ + { 0x0000, 0x0000, 0x0000 }, /* R238 */ + { 0x0000, 0x0000, 0x0000 }, /* R239 */ + { 0x0000, 0x0000, 0x0000 }, /* R240 */ + { 0x0000, 0x0000, 0x0000 }, /* R241 */ + { 0x0000, 0x0000, 0x0000 }, /* R242 */ + { 0x0000, 0x0000, 0x0000 }, /* R243 */ + { 0x0000, 0x0000, 0x0000 }, /* R244 */ + { 0x0000, 0x0000, 0x0000 }, /* R245 */ + { 0x0000, 0x0000, 0x0000 }, /* R246 */ + { 0x0000, 0x0000, 0x0000 }, /* R247 */ + { 0xFFFF, 0x0010, 0xFFFF }, /* R248 */ + { 0x0000, 0x0000, 0x0000 }, /* R249 */ + { 0xFFFF, 0x0010, 0xFFFF }, /* R250 */ + { 0xFFFF, 0x0010, 0xFFFF }, /* R251 */ + { 0x0000, 0x0000, 0x0000 }, /* R252 */ + { 0xFFFF, 0x0010, 0xFFFF }, /* R253 */ + { 0x0000, 0x0000, 0x0000 }, /* R254 */ + { 0x0000, 0x0000, 0x0000 }, /* R255 */ +}; diff --git a/drivers/mfd/wm8400-core.c b/drivers/mfd/wm8400-core.c new file mode 100644 index 000000000000..6a0cedb5bb8a --- /dev/null +++ b/drivers/mfd/wm8400-core.c @@ -0,0 +1,455 @@ +/* + * Core driver for WM8400. + * + * Copyright 2008 Wolfson Microelectronics PLC. + * + * Author: Mark Brown <broonie@opensource.wolfsonmicro.com> + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + */ + +#include <linux/bug.h> +#include <linux/i2c.h> +#include <linux/kernel.h> +#include <linux/mfd/wm8400-private.h> +#include <linux/mfd/wm8400-audio.h> + +static struct { + u16 readable; /* Mask of readable bits */ + u16 writable; /* Mask of writable bits */ + u16 vol; /* Mask of volatile bits */ + int is_codec; /* Register controlled by codec reset */ + u16 default_val; /* Value on reset */ +} reg_data[] = { + { 0xFFFF, 0xFFFF, 0x0000, 0, 0x6172 }, /* R0 */ + { 0x7000, 0x0000, 0x8000, 0, 0x0000 }, /* R1 */ + { 0xFF17, 0xFF17, 0x0000, 0, 0x0000 }, /* R2 */ + { 0xEBF3, 0xEBF3, 0x0000, 1, 0x6000 }, /* R3 */ + { 0x3CF3, 0x3CF3, 0x0000, 1, 0x0000 }, /* R4 */ + { 0xF1F8, 0xF1F8, 0x0000, 1, 0x4050 }, /* R5 */ + { 0xFC1F, 0xFC1F, 0x0000, 1, 0x4000 }, /* R6 */ + { 0xDFDE, 0xDFDE, 0x0000, 1, 0x01C8 }, /* R7 */ + { 0xFCFC, 0xFCFC, 0x0000, 1, 0x0000 }, /* R8 */ + { 0xEFFF, 0xEFFF, 0x0000, 1, 0x0040 }, /* R9 */ + { 0xEFFF, 0xEFFF, 0x0000, 1, 0x0040 }, /* R10 */ + { 0x27F7, 0x27F7, 0x0000, 1, 0x0004 }, /* R11 */ + { 0x01FF, 0x01FF, 0x0000, 1, 0x00C0 }, /* R12 */ + { 0x01FF, 0x01FF, 0x0000, 1, 0x00C0 }, /* R13 */ + { 0x1FEF, 0x1FEF, 0x0000, 1, 0x0000 }, /* R14 */ + { 0x0163, 0x0163, 0x0000, 1, 0x0100 }, /* R15 */ + { 0x01FF, 0x01FF, 0x0000, 1, 0x00C0 }, /* R16 */ + { 0x01FF, 0x01FF, 0x0000, 1, 0x00C0 }, /* R17 */ + { 0x1FFF, 0x0FFF, 0x0000, 1, 0x0000 }, /* R18 */ + { 0xFFFF, 0xFFFF, 0x0000, 1, 0x1000 }, /* R19 */ + { 0xFFFF, 0xFFFF, 0x0000, 1, 0x1010 }, /* R20 */ + { 0xFFFF, 0xFFFF, 0x0000, 1, 0x1010 }, /* R21 */ + { 0x0FDD, 0x0FDD, 0x0000, 1, 0x8000 }, /* R22 */ + { 0x1FFF, 0x1FFF, 0x0000, 1, 0x0800 }, /* R23 */ + { 0x0000, 0x01DF, 0x0000, 1, 0x008B }, /* R24 */ + { 0x0000, 0x01DF, 0x0000, 1, 0x008B }, /* R25 */ + { 0x0000, 0x01DF, 0x0000, 1, 0x008B }, /* R26 */ + { 0x0000, 0x01DF, 0x0000, 1, 0x008B }, /* R27 */ + { 0x0000, 0x01FF, 0x0000, 1, 0x0000 }, /* R28 */ + { 0x0000, 0x01FF, 0x0000, 1, 0x0000 }, /* R29 */ + { 0x0000, 0x0077, 0x0000, 1, 0x0066 }, /* R30 */ + { 0x0000, 0x0033, 0x0000, 1, 0x0022 }, /* R31 */ + { 0x0000, 0x01FF, 0x0000, 1, 0x0079 }, /* R32 */ + { 0x0000, 0x01FF, 0x0000, 1, 0x0079 }, /* R33 */ + { 0x0000, 0x0003, 0x0000, 1, 0x0003 }, /* R34 */ + { 0x0000, 0x01FF, 0x0000, 1, 0x0003 }, /* R35 */ + { 0x0000, 0x0000, 0x0000, 0, 0x0000 }, /* R36 */ + { 0x0000, 0x003F, 0x0000, 1, 0x0100 }, /* R37 */ + { 0x0000, 0x0000, 0x0000, 0, 0x0000 }, /* R38 */ + { 0x0000, 0x000F, 0x0000, 0, 0x0000 }, /* R39 */ + { 0x0000, 0x00FF, 0x0000, 1, 0x0000 }, /* R40 */ + { 0x0000, 0x01B7, 0x0000, 1, 0x0000 }, /* R41 */ + { 0x0000, 0x01B7, 0x0000, 1, 0x0000 }, /* R42 */ + { 0x0000, 0x01FF, 0x0000, 1, 0x0000 }, /* R43 */ + { 0x0000, 0x01FF, 0x0000, 1, 0x0000 }, /* R44 */ + { 0x0000, 0x00FD, 0x0000, 1, 0x0000 }, /* R45 */ + { 0x0000, 0x00FD, 0x0000, 1, 0x0000 }, /* R46 */ + { 0x0000, 0x01FF, 0x0000, 1, 0x0000 }, /* R47 */ + { 0x0000, 0x01FF, 0x0000, 1, 0x0000 }, /* R48 */ + { 0x0000, 0x01FF, 0x0000, 1, 0x0000 }, /* R49 */ + { 0x0000, 0x01FF, 0x0000, 1, 0x0000 }, /* R50 */ + { 0x0000, 0x01B3, 0x0000, 1, 0x0180 }, /* R51 */ + { 0x0000, 0x0077, 0x0000, 1, 0x0000 }, /* R52 */ + { 0x0000, 0x0077, 0x0000, 1, 0x0000 }, /* R53 */ + { 0x0000, 0x00FF, 0x0000, 1, 0x0000 }, /* R54 */ + { 0x0000, 0x0001, 0x0000, 1, 0x0000 }, /* R55 */ + { 0x0000, 0x003F, 0x0000, 1, 0x0000 }, /* R56 */ + { 0x0000, 0x004F, 0x0000, 1, 0x0000 }, /* R57 */ + { 0x0000, 0x00FD, 0x0000, 1, 0x0000 }, /* R58 */ + { 0x0000, 0x0000, 0x0000, 0, 0x0000 }, /* R59 */ + { 0x1FFF, 0x1FFF, 0x0000, 1, 0x0000 }, /* R60 */ + { 0xFFFF, 0xFFFF, 0x0000, 1, 0x0000 }, /* R61 */ + { 0x03FF, 0x03FF, 0x0000, 1, 0x0000 }, /* R62 */ + { 0x007F, 0x007F, 0x0000, 1, 0x0000 }, /* R63 */ + { 0x0000, 0x0000, 0x0000, 0, 0x0000 }, /* R64 */ + { 0xDFFF, 0xDFFF, 0x0000, 0, 0x0000 }, /* R65 */ + { 0xDFFF, 0xDFFF, 0x0000, 0, 0x0000 }, /* R66 */ + { 0xDFFF, 0xDFFF, 0x0000, 0, 0x0000 }, /* R67 */ + { 0xDFFF, 0xDFFF, 0x0000, 0, 0x0000 }, /* R68 */ + { 0x0000, 0x0000, 0x0000, 0, 0x0000 }, /* R69 */ + { 0xFFFF, 0xFFFF, 0x0000, 0, 0x4400 }, /* R70 */ + { 0x23FF, 0x23FF, 0x0000, 0, 0x0000 }, /* R71 */ + { 0xFFFF, 0xFFFF, 0x0000, 0, 0x4400 }, /* R72 */ + { 0x23FF, 0x23FF, 0x0000, 0, 0x0000 }, /* R73 */ + { 0x0000, 0x0000, 0x0000, 0, 0x0000 }, /* R74 */ + { 0x000E, 0x000E, 0x0000, 0, 0x0008 }, /* R75 */ + { 0xE00F, 0xE00F, 0x0000, 0, 0x0000 }, /* R76 */ + { 0x0000, 0x0000, 0x0000, 0, 0x0000 }, /* R77 */ + { 0x03C0, 0x03C0, 0x0000, 0, 0x02C0 }, /* R78 */ + { 0xFFFF, 0x0000, 0xffff, 0, 0x0000 }, /* R79 */ + { 0xFFFF, 0xFFFF, 0x0000, 0, 0x0000 }, /* R80 */ + { 0xFFFF, 0x0000, 0xffff, 0, 0x0000 }, /* R81 */ + { 0x2BFF, 0x0000, 0xffff, 0, 0x0000 }, /* R82 */ + { 0x0000, 0x0000, 0x0000, 0, 0x0000 }, /* R83 */ + { 0x80FF, 0x80FF, 0x0000, 0, 0x00ff }, /* R84 */ +}; + +static int wm8400_read(struct wm8400 *wm8400, u8 reg, int num_regs, u16 *dest) +{ + int i, ret = 0; + + BUG_ON(reg + num_regs - 1 > ARRAY_SIZE(wm8400->reg_cache)); + + /* If there are any volatile reads then read back the entire block */ + for (i = reg; i < reg + num_regs; i++) + if (reg_data[i].vol) { + ret = wm8400->read_dev(wm8400->io_data, reg, + num_regs, dest); + if (ret != 0) + return ret; + for (i = 0; i < num_regs; i++) + dest[i] = be16_to_cpu(dest[i]); + + return 0; + } + + /* Otherwise use the cache */ + memcpy(dest, &wm8400->reg_cache[reg], num_regs * sizeof(u16)); + + return 0; +} + +static int wm8400_write(struct wm8400 *wm8400, u8 reg, int num_regs, + u16 *src) +{ + int ret, i; + + BUG_ON(reg + num_regs - 1 > ARRAY_SIZE(wm8400->reg_cache)); + + for (i = 0; i < num_regs; i++) { + BUG_ON(!reg_data[reg + i].writable); + wm8400->reg_cache[reg + i] = src[i]; + src[i] = cpu_to_be16(src[i]); + } + + /* Do the actual I/O */ + ret = wm8400->write_dev(wm8400->io_data, reg, num_regs, src); + if (ret != 0) + return -EIO; + + return 0; +} + +/** + * wm8400_reg_read - Single register read + * + * @wm8400: Pointer to wm8400 control structure + * @reg: Register to read + * + * @return Read value + */ +u16 wm8400_reg_read(struct wm8400 *wm8400, u8 reg) +{ + u16 val; + + mutex_lock(&wm8400->io_lock); + + wm8400_read(wm8400, reg, 1, &val); + + mutex_unlock(&wm8400->io_lock); + + return val; +} +EXPORT_SYMBOL_GPL(wm8400_reg_read); + +int wm8400_block_read(struct wm8400 *wm8400, u8 reg, int count, u16 *data) +{ + int ret; + + mutex_lock(&wm8400->io_lock); + + ret = wm8400_read(wm8400, reg, count, data); + + mutex_unlock(&wm8400->io_lock); + + return ret; +} +EXPORT_SYMBOL_GPL(wm8400_block_read); + +/** + * wm8400_set_bits - Bitmask write + * + * @wm8400: Pointer to wm8400 control structure + * @reg: Register to access + * @mask: Mask of bits to change + * @val: Value to set for masked bits + */ +int wm8400_set_bits(struct wm8400 *wm8400, u8 reg, u16 mask, u16 val) +{ + u16 tmp; + int ret; + + mutex_lock(&wm8400->io_lock); + + ret = wm8400_read(wm8400, reg, 1, &tmp); + tmp = (tmp & ~mask) | val; + if (ret == 0) + ret = wm8400_write(wm8400, reg, 1, &tmp); + + mutex_unlock(&wm8400->io_lock); + + return ret; +} +EXPORT_SYMBOL_GPL(wm8400_set_bits); + +/** + * wm8400_reset_codec_reg_cache - Reset cached codec registers to + * their default values. + */ +void wm8400_reset_codec_reg_cache(struct wm8400 *wm8400) +{ + int i; + + mutex_lock(&wm8400->io_lock); + + /* Reset all codec registers to their initial value */ + for (i = 0; i < ARRAY_SIZE(wm8400->reg_cache); i++) + if (reg_data[i].is_codec) + wm8400->reg_cache[i] = reg_data[i].default_val; + + mutex_unlock(&wm8400->io_lock); +} +EXPORT_SYMBOL_GPL(wm8400_reset_codec_reg_cache); + +/* + * wm8400_init - Generic initialisation + * + * The WM8400 can be configured as either an I2C or SPI device. Probe + * functions for each bus set up the accessors then call into this to + * set up the device itself. + */ +static int wm8400_init(struct wm8400 *wm8400, + struct wm8400_platform_data *pdata) +{ + u16 reg; + int ret, i; + + mutex_init(&wm8400->io_lock); + + wm8400->dev->driver_data = wm8400; + + /* Check that this is actually a WM8400 */ + ret = wm8400->read_dev(wm8400->io_data, WM8400_RESET_ID, 1, ®); + if (ret != 0) { + dev_err(wm8400->dev, "Chip ID register read failed\n"); + return -EIO; + } + if (be16_to_cpu(reg) != reg_data[WM8400_RESET_ID].default_val) { + dev_err(wm8400->dev, "Device is not a WM8400, ID is %x\n", + be16_to_cpu(reg)); + return -ENODEV; + } + + /* We don't know what state the hardware is in and since this + * is a PMIC we can't reset it safely so initialise the register + * cache from the hardware. + */ + ret = wm8400->read_dev(wm8400->io_data, 0, + ARRAY_SIZE(wm8400->reg_cache), + wm8400->reg_cache); + if (ret != 0) { + dev_err(wm8400->dev, "Register cache read failed\n"); + return -EIO; + } + for (i = 0; i < ARRAY_SIZE(wm8400->reg_cache); i++) + wm8400->reg_cache[i] = be16_to_cpu(wm8400->reg_cache[i]); + + /* If the codec is in reset use hard coded values */ + if (!(wm8400->reg_cache[WM8400_POWER_MANAGEMENT_1] & WM8400_CODEC_ENA)) + for (i = 0; i < ARRAY_SIZE(wm8400->reg_cache); i++) + if (reg_data[i].is_codec) + wm8400->reg_cache[i] = reg_data[i].default_val; + + ret = wm8400_read(wm8400, WM8400_ID, 1, ®); + if (ret != 0) { + dev_err(wm8400->dev, "ID register read failed: %d\n", ret); + return ret; + } + reg = (reg & WM8400_CHIP_REV_MASK) >> WM8400_CHIP_REV_SHIFT; + dev_info(wm8400->dev, "WM8400 revision %x\n", reg); + + if (pdata && pdata->platform_init) { + ret = pdata->platform_init(wm8400->dev); + if (ret != 0) + dev_err(wm8400->dev, "Platform init failed: %d\n", + ret); + } else + dev_warn(wm8400->dev, "No platform initialisation supplied\n"); + + return ret; +} + +static void wm8400_release(struct wm8400 *wm8400) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(wm8400->regulators); i++) + if (wm8400->regulators[i].name) + platform_device_unregister(&wm8400->regulators[i]); +} + +#if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE) +static int wm8400_i2c_read(void *io_data, char reg, int count, u16 *dest) +{ + struct i2c_client *i2c = io_data; + struct i2c_msg xfer[2]; + int ret; + + /* Write register */ + xfer[0].addr = i2c->addr; + xfer[0].flags = 0; + xfer[0].len = 1; + xfer[0].buf = ® + + /* Read data */ + xfer[1].addr = i2c->addr; + xfer[1].flags = I2C_M_RD; + xfer[1].len = count * sizeof(u16); + xfer[1].buf = (u8 *)dest; + + ret = i2c_transfer(i2c->adapter, xfer, 2); + if (ret == 2) + ret = 0; + else if (ret >= 0) + ret = -EIO; + + return ret; +} + +static int wm8400_i2c_write(void *io_data, char reg, int count, const u16 *src) +{ + struct i2c_client *i2c = io_data; + u8 *msg; + int ret; + + /* We add 1 byte for device register - ideally I2C would gather. */ + msg = kmalloc((count * sizeof(u16)) + 1, GFP_KERNEL); + if (msg == NULL) + return -ENOMEM; + + msg[0] = reg; + memcpy(&msg[1], src, count * sizeof(u16)); + + ret = i2c_master_send(i2c, msg, (count * sizeof(u16)) + 1); + + if (ret == (count * 2) + 1) + ret = 0; + else if (ret >= 0) + ret = -EIO; + + kfree(msg); + + return ret; +} + +static int wm8400_i2c_probe(struct i2c_client *i2c, + const struct i2c_device_id *id) +{ + struct wm8400 *wm8400; + int ret; + + wm8400 = kzalloc(sizeof(struct wm8400), GFP_KERNEL); + if (wm8400 == NULL) { + ret = -ENOMEM; + goto err; + } + + wm8400->io_data = i2c; + wm8400->read_dev = wm8400_i2c_read; + wm8400->write_dev = wm8400_i2c_write; + wm8400->dev = &i2c->dev; + i2c_set_clientdata(i2c, wm8400); + + ret = wm8400_init(wm8400, i2c->dev.platform_data); + if (ret != 0) + goto struct_err; + + return 0; + +struct_err: + i2c_set_clientdata(i2c, NULL); + kfree(wm8400); +err: + return ret; +} + +static int wm8400_i2c_remove(struct i2c_client *i2c) +{ + struct wm8400 *wm8400 = i2c_get_clientdata(i2c); + + wm8400_release(wm8400); + i2c_set_clientdata(i2c, NULL); + kfree(wm8400); + + return 0; +} + +static const struct i2c_device_id wm8400_i2c_id[] = { + { "wm8400", 0 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, wm8400_i2c_id); + +static struct i2c_driver wm8400_i2c_driver = { + .driver = { + .name = "WM8400", + .owner = THIS_MODULE, + }, + .probe = wm8400_i2c_probe, + .remove = wm8400_i2c_remove, + .id_table = wm8400_i2c_id, +}; +#endif + +static int __init wm8400_module_init(void) +{ + int ret = -ENODEV; + +#if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE) + ret = i2c_add_driver(&wm8400_i2c_driver); + if (ret != 0) + pr_err("Failed to register I2C driver: %d\n", ret); +#endif + + return ret; +} +module_init(wm8400_module_init); + +static void __exit wm8400_module_exit(void) +{ +#if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE) + i2c_del_driver(&wm8400_i2c_driver); +#endif +} +module_exit(wm8400_module_exit); + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Mark Brown <broonie@opensource.wolfsonmicro.com>"); diff --git a/drivers/net/3c501.c b/drivers/net/3c501.c index 5ba4bab6d43e..7d15e7c6bcad 100644 --- a/drivers/net/3c501.c +++ b/drivers/net/3c501.c @@ -17,7 +17,7 @@ Annapolis MD 21403 Fixed (again!) the missing interrupt locking on TX/RX shifting. - Alan Cox <Alan.Cox@linux.org> + Alan Cox <alan@lxorguk.ukuu.org.uk> Removed calls to init_etherdev since they are no longer needed, and cleaned up modularization just a bit. The driver still allows only @@ -29,16 +29,16 @@ the board. Now getting 150K/second FTP with a 3c501 card. Still playing with a TX-TX optimisation to see if we can touch 180-200K/second as seems theoretically maximum. - 19950402 Alan Cox <Alan.Cox@linux.org> + 19950402 Alan Cox <alan@lxorguk.ukuu.org.uk> Cleaned up for 2.3.x because we broke SMP now. - 20000208 Alan Cox <alan@redhat.com> + 20000208 Alan Cox <alan@lxorguk.ukuu.org.uk> Check up pass for 2.5. Nothing significant changed - 20021009 Alan Cox <alan@redhat.com> + 20021009 Alan Cox <alan@lxorguk.ukuu.org.uk> Fixed zero fill corner case - 20030104 Alan Cox <alan@redhat.com> + 20030104 Alan Cox <alan@lxorguk.ukuu.org.uk> For the avoidance of doubt the "preferred form" of this code is one which @@ -104,7 +104,7 @@ static const char version[] = - DRV_NAME ".c: " DRV_VERSION " Alan Cox (alan@redhat.com).\n"; + DRV_NAME ".c: " DRV_VERSION " Alan Cox (alan@lxorguk.ukuu.org.uk).\n"; /* * Braindamage remaining: diff --git a/drivers/net/3c515.c b/drivers/net/3c515.c index e4e3241628d6..a0f8b6e2d0af 100644 --- a/drivers/net/3c515.c +++ b/drivers/net/3c515.c @@ -18,7 +18,7 @@ 2001/11/17 - Added ethtool support (jgarzik) - 2002/10/28 - Locking updates for 2.5 (alan@redhat.com) + 2002/10/28 - Locking updates for 2.5 (alan@lxorguk.ukuu.org.uk) */ diff --git a/drivers/net/Kconfig b/drivers/net/Kconfig index e9d529442b06..1d8af3348331 100644 --- a/drivers/net/Kconfig +++ b/drivers/net/Kconfig @@ -2400,7 +2400,7 @@ config EHEA will be called ehea. config ENIC - tristate "E, the Cisco 10G Ethernet NIC" + tristate "Cisco 10G Ethernet NIC support" depends on PCI && INET select INET_LRO help diff --git a/drivers/net/appletalk/cops.c b/drivers/net/appletalk/cops.c index a0b4c8516073..735fc9476403 100644 --- a/drivers/net/appletalk/cops.c +++ b/drivers/net/appletalk/cops.c @@ -4,7 +4,7 @@ * - Jay Schulist <jschlst@samba.org> * * With more than a little help from; - * - Alan Cox <Alan.Cox@linux.org> + * - Alan Cox <alan@lxorguk.ukuu.org.uk> * * Derived from: * - skeleton.c: A network driver outline for linux. diff --git a/drivers/net/cxgb3/adapter.h b/drivers/net/cxgb3/adapter.h index 455ef529cd62..bc8e2413abd2 100644 --- a/drivers/net/cxgb3/adapter.h +++ b/drivers/net/cxgb3/adapter.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003-2007 Chelsio, Inc. All rights reserved. + * Copyright (c) 2003-2008 Chelsio, Inc. All rights reserved. * * This software is available to you under a choice of one of two * licenses. You may choose to be licensed under the terms of the GNU diff --git a/drivers/net/cxgb3/ael1002.c b/drivers/net/cxgb3/ael1002.c index 744fac0b1617..5c3c05da4d96 100644 --- a/drivers/net/cxgb3/ael1002.c +++ b/drivers/net/cxgb3/ael1002.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2005-2007 Chelsio, Inc. All rights reserved. + * Copyright (c) 2005-2008 Chelsio, Inc. All rights reserved. * * This software is available to you under a choice of one of two * licenses. You may choose to be licensed under the terms of the GNU diff --git a/drivers/net/cxgb3/common.h b/drivers/net/cxgb3/common.h index 593fb643a615..e312d315a42d 100644 --- a/drivers/net/cxgb3/common.h +++ b/drivers/net/cxgb3/common.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2005-2007 Chelsio, Inc. All rights reserved. + * Copyright (c) 2005-2008 Chelsio, Inc. All rights reserved. * * This software is available to you under a choice of one of two * licenses. You may choose to be licensed under the terms of the GNU diff --git a/drivers/net/cxgb3/cxgb3_ctl_defs.h b/drivers/net/cxgb3/cxgb3_ctl_defs.h index 6ad92405d9a0..1d8d46eb3c96 100644 --- a/drivers/net/cxgb3/cxgb3_ctl_defs.h +++ b/drivers/net/cxgb3/cxgb3_ctl_defs.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003-2007 Chelsio, Inc. All rights reserved. + * Copyright (c) 2003-2008 Chelsio, Inc. All rights reserved. * * This software is available to you under a choice of one of two * licenses. You may choose to be licensed under the terms of the GNU diff --git a/drivers/net/cxgb3/cxgb3_defs.h b/drivers/net/cxgb3/cxgb3_defs.h index 45e92164c260..47e53769af5b 100644 --- a/drivers/net/cxgb3/cxgb3_defs.h +++ b/drivers/net/cxgb3/cxgb3_defs.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2006-2007 Chelsio, Inc. All rights reserved. + * Copyright (c) 2006-2008 Chelsio, Inc. All rights reserved. * * This software is available to you under a choice of one of two * licenses. You may choose to be licensed under the terms of the GNU diff --git a/drivers/net/cxgb3/cxgb3_ioctl.h b/drivers/net/cxgb3/cxgb3_ioctl.h index 3e8d5faec3a4..b19e4376ba76 100644 --- a/drivers/net/cxgb3/cxgb3_ioctl.h +++ b/drivers/net/cxgb3/cxgb3_ioctl.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003-2007 Chelsio, Inc. All rights reserved. + * Copyright (c) 2003-2008 Chelsio, Inc. All rights reserved. * * This software is available to you under a choice of one of two * licenses. You may choose to be licensed under the terms of the GNU diff --git a/drivers/net/cxgb3/cxgb3_main.c b/drivers/net/cxgb3/cxgb3_main.c index f31985df0bb9..1ace41a13ac3 100644 --- a/drivers/net/cxgb3/cxgb3_main.c +++ b/drivers/net/cxgb3/cxgb3_main.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003-2007 Chelsio, Inc. All rights reserved. + * Copyright (c) 2003-2008 Chelsio, Inc. All rights reserved. * * This software is available to you under a choice of one of two * licenses. You may choose to be licensed under the terms of the GNU diff --git a/drivers/net/cxgb3/cxgb3_offload.c b/drivers/net/cxgb3/cxgb3_offload.c index 0f6fd63b2847..265aa8a15afa 100644 --- a/drivers/net/cxgb3/cxgb3_offload.c +++ b/drivers/net/cxgb3/cxgb3_offload.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2006-2007 Chelsio, Inc. All rights reserved. + * Copyright (c) 2006-2008 Chelsio, Inc. All rights reserved. * * This software is available to you under a choice of one of two * licenses. You may choose to be licensed under the terms of the GNU diff --git a/drivers/net/cxgb3/cxgb3_offload.h b/drivers/net/cxgb3/cxgb3_offload.h index 7a379138b5a6..d514e5019dfc 100644 --- a/drivers/net/cxgb3/cxgb3_offload.h +++ b/drivers/net/cxgb3/cxgb3_offload.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2006-2007 Chelsio, Inc. All rights reserved. + * Copyright (c) 2006-2008 Chelsio, Inc. All rights reserved. * * This software is available to you under a choice of one of two * licenses. You may choose to be licensed under the terms of the GNU diff --git a/drivers/net/cxgb3/firmware_exports.h b/drivers/net/cxgb3/firmware_exports.h index b75ddd8777fe..0d9b0e6dccff 100644 --- a/drivers/net/cxgb3/firmware_exports.h +++ b/drivers/net/cxgb3/firmware_exports.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2004-2007 Chelsio, Inc. All rights reserved. + * Copyright (c) 2004-2008 Chelsio, Inc. All rights reserved. * * This software is available to you under a choice of one of two * licenses. You may choose to be licensed under the terms of the GNU diff --git a/drivers/net/cxgb3/l2t.c b/drivers/net/cxgb3/l2t.c index b2c5314582aa..4407ac9bb555 100644 --- a/drivers/net/cxgb3/l2t.c +++ b/drivers/net/cxgb3/l2t.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003-2007 Chelsio, Inc. All rights reserved. + * Copyright (c) 2003-2008 Chelsio, Inc. All rights reserved. * * This software is available to you under a choice of one of two * licenses. You may choose to be licensed under the terms of the GNU diff --git a/drivers/net/cxgb3/l2t.h b/drivers/net/cxgb3/l2t.h index 42ce65f76a87..fd3eb07e3f40 100644 --- a/drivers/net/cxgb3/l2t.h +++ b/drivers/net/cxgb3/l2t.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003-2007 Chelsio, Inc. All rights reserved. + * Copyright (c) 2003-2008 Chelsio, Inc. All rights reserved. * * This software is available to you under a choice of one of two * licenses. You may choose to be licensed under the terms of the GNU diff --git a/drivers/net/cxgb3/mc5.c b/drivers/net/cxgb3/mc5.c index 4c4d6e877ea6..3b5517b8fbde 100644 --- a/drivers/net/cxgb3/mc5.c +++ b/drivers/net/cxgb3/mc5.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003-2007 Chelsio, Inc. All rights reserved. + * Copyright (c) 2003-2008 Chelsio, Inc. All rights reserved. * * This software is available to you under a choice of one of two * licenses. You may choose to be licensed under the terms of the GNU diff --git a/drivers/net/cxgb3/sge.c b/drivers/net/cxgb3/sge.c index 87919419b707..c6480be0bc1f 100644 --- a/drivers/net/cxgb3/sge.c +++ b/drivers/net/cxgb3/sge.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2005-2007 Chelsio, Inc. All rights reserved. + * Copyright (c) 2005-2008 Chelsio, Inc. All rights reserved. * * This software is available to you under a choice of one of two * licenses. You may choose to be licensed under the terms of the GNU diff --git a/drivers/net/cxgb3/t3_cpl.h b/drivers/net/cxgb3/t3_cpl.h index 917970ed24a1..852c399a8b0a 100644 --- a/drivers/net/cxgb3/t3_cpl.h +++ b/drivers/net/cxgb3/t3_cpl.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2004-2007 Chelsio, Inc. All rights reserved. + * Copyright (c) 2004-2008 Chelsio, Inc. All rights reserved. * * This software is available to you under a choice of one of two * licenses. You may choose to be licensed under the terms of the GNU diff --git a/drivers/net/cxgb3/t3_hw.c b/drivers/net/cxgb3/t3_hw.c index 4da5b09b9bc2..968f64be3743 100644 --- a/drivers/net/cxgb3/t3_hw.c +++ b/drivers/net/cxgb3/t3_hw.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003-2007 Chelsio, Inc. All rights reserved. + * Copyright (c) 2003-2008 Chelsio, Inc. All rights reserved. * * This software is available to you under a choice of one of two * licenses. You may choose to be licensed under the terms of the GNU diff --git a/drivers/net/cxgb3/t3cdev.h b/drivers/net/cxgb3/t3cdev.h index 0a21cfbd2b21..be55e9ae74d1 100644 --- a/drivers/net/cxgb3/t3cdev.h +++ b/drivers/net/cxgb3/t3cdev.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2006-2007 Chelsio Communications. All rights reserved. + * Copyright (C) 2006-2008 Chelsio Communications. All rights reserved. * * This software is available to you under a choice of one of two * licenses. You may choose to be licensed under the terms of the GNU diff --git a/drivers/net/cxgb3/version.h b/drivers/net/cxgb3/version.h index 29db711303b9..bb8698a86754 100644 --- a/drivers/net/cxgb3/version.h +++ b/drivers/net/cxgb3/version.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003-2007 Chelsio, Inc. All rights reserved. + * Copyright (c) 2003-2008 Chelsio, Inc. All rights reserved. * * This software is available to you under a choice of one of two * licenses. You may choose to be licensed under the terms of the GNU @@ -35,7 +35,7 @@ #define DRV_DESC "Chelsio T3 Network Driver" #define DRV_NAME "cxgb3" /* Driver version */ -#define DRV_VERSION "1.0-ko" +#define DRV_VERSION "1.1.0-ko" /* Firmware version */ #define FW_VERSION_MAJOR 7 diff --git a/drivers/net/cxgb3/vsc8211.c b/drivers/net/cxgb3/vsc8211.c index 306c2dc4ab34..33f956bd6b59 100644 --- a/drivers/net/cxgb3/vsc8211.c +++ b/drivers/net/cxgb3/vsc8211.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2005-2007 Chelsio, Inc. All rights reserved. + * Copyright (c) 2005-2008 Chelsio, Inc. All rights reserved. * * This software is available to you under a choice of one of two * licenses. You may choose to be licensed under the terms of the GNU diff --git a/drivers/net/cxgb3/xgmac.c b/drivers/net/cxgb3/xgmac.c index ffdc0a1892bd..9d7786937aad 100644 --- a/drivers/net/cxgb3/xgmac.c +++ b/drivers/net/cxgb3/xgmac.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2005-2007 Chelsio, Inc. All rights reserved. + * Copyright (c) 2005-2008 Chelsio, Inc. All rights reserved. * * This software is available to you under a choice of one of two * licenses. You may choose to be licensed under the terms of the GNU diff --git a/drivers/net/eexpress.c b/drivers/net/eexpress.c index 795c594a4b7c..b751c1b96cfa 100644 --- a/drivers/net/eexpress.c +++ b/drivers/net/eexpress.c @@ -8,7 +8,7 @@ * * Many modifications, and currently maintained, by * Philip Blundell <philb@gnu.org> - * Added the Compaq LTE Alan Cox <alan@redhat.com> + * Added the Compaq LTE Alan Cox <alan@lxorguk.ukuu.org.uk> * Added MCA support Adam Fritzler * * Note - this driver is experimental still - it has problems on faster diff --git a/drivers/net/enic/enic_main.c b/drivers/net/enic/enic_main.c index f3a47a87dbbe..180e968dc54d 100644 --- a/drivers/net/enic/enic_main.c +++ b/drivers/net/enic/enic_main.c @@ -34,6 +34,7 @@ #include <linux/ip.h> #include <linux/ipv6.h> #include <linux/tcp.h> +#include <net/ip6_checksum.h> #include "cq_enet_desc.h" #include "vnic_dev.h" diff --git a/drivers/net/ibmlana.c b/drivers/net/ibmlana.c index 95e3464068db..f02764725a22 100644 --- a/drivers/net/ibmlana.c +++ b/drivers/net/ibmlana.c @@ -71,7 +71,7 @@ History: June 1st, 2000 corrected version codes, added support for the latest 2.3 changes Oct 28th, 2002 - cleaned up for the 2.5 tree <alan@redhat.com> + cleaned up for the 2.5 tree <alan@lxorguk.ukuu.org.uk> *************************************************************************/ diff --git a/drivers/net/jme.c b/drivers/net/jme.c index 156f159aafbb..81c6cdc3851f 100644 --- a/drivers/net/jme.c +++ b/drivers/net/jme.c @@ -37,6 +37,7 @@ #include <linux/tcp.h> #include <linux/udp.h> #include <linux/if_vlan.h> +#include <net/ip6_checksum.h> #include "jme.h" static int force_pseudohp = -1; diff --git a/drivers/net/macmace.c b/drivers/net/macmace.c index 51ad3765e075..85587a6667b9 100644 --- a/drivers/net/macmace.c +++ b/drivers/net/macmace.c @@ -9,7 +9,7 @@ * 2 of the License, or (at your option) any later version. * * Copyright (C) 1996 Paul Mackerras. - * Copyright (C) 1998 Alan Cox <alan@redhat.com> + * Copyright (C) 1998 Alan Cox <alan@lxorguk.ukuu.org.uk> * * Modified heavily by Joshua M. Thompson based on Dave Huang's NetBSD driver * diff --git a/drivers/net/pcmcia/3c589_cs.c b/drivers/net/pcmcia/3c589_cs.c index 0b28d0d8ffa8..c235cdba69c6 100644 --- a/drivers/net/pcmcia/3c589_cs.c +++ b/drivers/net/pcmcia/3c589_cs.c @@ -15,7 +15,7 @@ incorporated herein by reference. Donald Becker may be reached at becker@scyld.com - Updated for 2.5.x by Alan Cox <alan@redhat.com> + Updated for 2.5.x by Alan Cox <alan@lxorguk.ukuu.org.uk> ======================================================================*/ diff --git a/drivers/net/pcmcia/nmclan_cs.c b/drivers/net/pcmcia/nmclan_cs.c index 54df34f21c5f..448cd40aeba5 100644 --- a/drivers/net/pcmcia/nmclan_cs.c +++ b/drivers/net/pcmcia/nmclan_cs.c @@ -69,7 +69,7 @@ Driver Notes and Issues History ------------------------------------------------------------------------------- Log: nmclan_cs.c,v - * 2.5.75-ac1 2003/07/11 Alan Cox <alan@redhat.com> + * 2.5.75-ac1 2003/07/11 Alan Cox <alan@lxorguk.ukuu.org.uk> * Fixed hang on card eject as we probe it * Cleaned up to use new style locking. * diff --git a/drivers/net/phy/mdio_bus.c b/drivers/net/phy/mdio_bus.c index 6671e2da0d57..d0ed1ef284a8 100644 --- a/drivers/net/phy/mdio_bus.c +++ b/drivers/net/phy/mdio_bus.c @@ -55,6 +55,7 @@ EXPORT_SYMBOL(mdiobus_alloc); /** * mdiobus_release - mii_bus device release callback + * @d: the target struct device that contains the mii_bus * * Description: called when the last reference to an mii_bus is * dropped, to free the underlying memory. diff --git a/drivers/net/phy/phy_device.c b/drivers/net/phy/phy_device.c index 171627480058..f11e900b437b 100644 --- a/drivers/net/phy/phy_device.c +++ b/drivers/net/phy/phy_device.c @@ -557,6 +557,7 @@ int genphy_restart_aneg(struct phy_device *phydev) return ctl; } +EXPORT_SYMBOL(genphy_restart_aneg); /** diff --git a/drivers/net/qlge/qlge.h b/drivers/net/qlge/qlge.h index c37ea436c918..38116f9d4163 100644 --- a/drivers/net/qlge/qlge.h +++ b/drivers/net/qlge/qlge.h @@ -58,7 +58,7 @@ */ #if (PAGE_SHIFT == 12) || (PAGE_SHIFT == 13) /* 4k & 8k pages */ #define TX_DESC_PER_OAL ((MAX_SKB_FRAGS - TX_DESC_PER_IOCB) + 2) -#elif (PAGE_SHIFT == 16) /* 64k pages */ +#else /* all other page sizes */ #define TX_DESC_PER_OAL 0 #endif diff --git a/drivers/net/qlge/qlge_main.c b/drivers/net/qlge/qlge_main.c index 297877b68c46..4b2caa6b7ac5 100644 --- a/drivers/net/qlge/qlge_main.c +++ b/drivers/net/qlge/qlge_main.c @@ -39,6 +39,7 @@ #include <linux/delay.h> #include <linux/mm.h> #include <linux/vmalloc.h> +#include <net/ip6_checksum.h> #include "qlge.h" diff --git a/drivers/net/tlan.c b/drivers/net/tlan.c index ec871f646766..c41d68761364 100644 --- a/drivers/net/tlan.c +++ b/drivers/net/tlan.c @@ -29,7 +29,8 @@ * * Tigran Aivazian <tigran@sco.com>: TLan_PciProbe() now uses * new PCI BIOS interface. - * Alan Cox <alan@redhat.com>: Fixed the out of memory + * Alan Cox <alan@lxorguk.ukuu.org.uk>: + * Fixed the out of memory * handling. * * Torben Mathiasen <torben.mathiasen@compaq.com> New Maintainer! diff --git a/drivers/net/tokenring/smctr.c b/drivers/net/tokenring/smctr.c index fa73e6eed6be..ed50d288e494 100644 --- a/drivers/net/tokenring/smctr.c +++ b/drivers/net/tokenring/smctr.c @@ -25,7 +25,7 @@ * To do: * 1. Multicast support. * - * Initial 2.5 cleanup Alan Cox <alan@redhat.com> 2002/10/28 + * Initial 2.5 cleanup Alan Cox <alan@lxorguk.ukuu.org.uk> 2002/10/28 */ #include <linux/module.h> diff --git a/drivers/net/tulip/de2104x.c b/drivers/net/tulip/de2104x.c index f54c45049d50..124d5d690dde 100644 --- a/drivers/net/tulip/de2104x.c +++ b/drivers/net/tulip/de2104x.c @@ -1688,6 +1688,7 @@ static void __devinit de21040_get_mac_address (struct de_private *de) unsigned i; dw32 (ROMCmd, 0); /* Reset the pointer with a dummy write. */ + udelay(5); for (i = 0; i < 6; i++) { int value, boguscnt = 100000; diff --git a/drivers/net/tulip/dmfe.c b/drivers/net/tulip/dmfe.c index 656200472fa1..8e46a513a252 100644 --- a/drivers/net/tulip/dmfe.c +++ b/drivers/net/tulip/dmfe.c @@ -23,7 +23,7 @@ Marcelo Tosatti <marcelo@conectiva.com.br> : Made it compile in 2.3 (device to net_device) - Alan Cox <alan@redhat.com> : + Alan Cox <alan@lxorguk.ukuu.org.uk> : Cleaned up for kernel merge. Removed the back compatibility support Reformatted, fixing spelling etc as I went @@ -49,7 +49,7 @@ support. Updated PCI resource allocation. Do not forget to unmap PCI mapped skbs. - Alan Cox <alan@redhat.com> + Alan Cox <alan@lxorguk.ukuu.org.uk> Added new PCI identifiers provided by Clear Zhang at ALi for their 1563 ethernet device. diff --git a/drivers/net/via-velocity.c b/drivers/net/via-velocity.c index ad20f96edfa1..2dced383bcfb 100644 --- a/drivers/net/via-velocity.c +++ b/drivers/net/via-velocity.c @@ -12,7 +12,7 @@ * Scatter gather * More testing * - * The changes are (c) Copyright 2004, Red Hat Inc. <alan@redhat.com> + * The changes are (c) Copyright 2004, Red Hat Inc. <alan@lxorguk.ukuu.org.uk> * Additional fixes and clean up: Francois Romieu * * This source has not been verified for use in safety critical systems. diff --git a/drivers/net/wan/z85230.c b/drivers/net/wan/z85230.c index 243bd8d918fe..ccd9cd35ecbe 100644 --- a/drivers/net/wan/z85230.c +++ b/drivers/net/wan/z85230.c @@ -18,7 +18,8 @@ * DMA now uses get_free_page as kmalloc buffers may span a 64K * boundary. * - * Modified for SMP safety and SMP locking by Alan Cox <alan@redhat.com> + * Modified for SMP safety and SMP locking by Alan Cox + * <alan@lxorguk.ukuu.org.uk> * * Performance * diff --git a/drivers/net/wan/z85230.h b/drivers/net/wan/z85230.h index 4f372396c512..85b3e785d484 100644 --- a/drivers/net/wan/z85230.h +++ b/drivers/net/wan/z85230.h @@ -2,7 +2,7 @@ * Description of Z8530 Z85C30 and Z85230 communications chips * * Copyright (C) 1995 David S. Miller (davem@caip.rutgers.edu) - * Copyright (C) 1998 Alan Cox <alan@redhat.com> + * Copyright (C) 1998 Alan Cox <alan@lxorguk.ukuu.org.uk> */ #ifndef _Z8530_H diff --git a/drivers/net/wireless/wavelan.c b/drivers/net/wireless/wavelan.c index 136220b5ca81..e939a73ff794 100644 --- a/drivers/net/wireless/wavelan.c +++ b/drivers/net/wireless/wavelan.c @@ -4387,7 +4387,7 @@ MODULE_LICENSE("GPL"); * * Thanks go also to: * James Ashton (jaa101@syseng.anu.edu.au), - * Alan Cox (alan@redhat.com), + * Alan Cox (alan@lxorguk.ukuu.org.uk), * Allan Creighton (allanc@cs.usyd.edu.au), * Matthew Geier (matthew@cs.usyd.edu.au), * Remo di Giovanni (remo@cs.usyd.edu.au), diff --git a/drivers/net/wireless/wavelan.p.h b/drivers/net/wireless/wavelan.p.h index b33ac47dd8df..44d31bbf39e4 100644 --- a/drivers/net/wireless/wavelan.p.h +++ b/drivers/net/wireless/wavelan.p.h @@ -186,7 +186,7 @@ * * Thanks go also to: * James Ashton <jaa101@syseng.anu.edu.au>, - * Alan Cox <alan@redhat.com>, + * Alan Cox <alan@lxorguk.ukuu.org.uk>, * Allan Creighton <allanc@cs.usyd.edu.au>, * Matthew Geier <matthew@cs.usyd.edu.au>, * Remo di Giovanni <remo@cs.usyd.edu.au>, diff --git a/drivers/regulator/Kconfig b/drivers/regulator/Kconfig index a656128f1fdd..4dada6ee1119 100644 --- a/drivers/regulator/Kconfig +++ b/drivers/regulator/Kconfig @@ -56,4 +56,28 @@ config REGULATOR_BQ24022 charging select between 100 mA and 500 mA charging current limit. +config REGULATOR_WM8350 + tristate "Wolfson Microelectroncis WM8350 AudioPlus PMIC" + depends on MFD_WM8350 + select REGULATOR + help + This driver provides support for the voltage and current regulators + of the WM8350 AudioPlus PMIC. + +config REGULATOR_WM8400 + tristate "Wolfson Microelectroncis WM8400 AudioPlus PMIC" + depends on MFD_WM8400 + select REGULATOR + help + This driver provides support for the voltage regulators of the + WM8400 AudioPlus PMIC. + +config REGULATOR_DA903X + tristate "Support regulators on Dialog Semiconductor DA9030/DA9034 PMIC" + depends on PMIC_DA903X + select REGULATOR + help + Say y here to support the BUCKs and LDOs regulators found on + Dialog Semiconductor DA9030/DA9034 PMIC. + endmenu diff --git a/drivers/regulator/Makefile b/drivers/regulator/Makefile index ac2c64efe65c..254d40c02ee8 100644 --- a/drivers/regulator/Makefile +++ b/drivers/regulator/Makefile @@ -8,5 +8,8 @@ obj-$(CONFIG_REGULATOR_FIXED_VOLTAGE) += fixed.o obj-$(CONFIG_REGULATOR_VIRTUAL_CONSUMER) += virtual.o obj-$(CONFIG_REGULATOR_BQ24022) += bq24022.o +obj-$(CONFIG_REGULATOR_WM8350) += wm8350-regulator.o +obj-$(CONFIG_REGULATOR_WM8400) += wm8400-regulator.o +obj-$(CONFIG_REGULATOR_DA903X) += da903x.o ccflags-$(CONFIG_REGULATOR_DEBUG) += -DDEBUG diff --git a/drivers/regulator/bq24022.c b/drivers/regulator/bq24022.c index 263699d6152d..366565aba865 100644 --- a/drivers/regulator/bq24022.c +++ b/drivers/regulator/bq24022.c @@ -18,13 +18,13 @@ #include <linux/regulator/bq24022.h> #include <linux/regulator/driver.h> + static int bq24022_set_current_limit(struct regulator_dev *rdev, int min_uA, int max_uA) { - struct platform_device *pdev = rdev_get_drvdata(rdev); - struct bq24022_mach_info *pdata = pdev->dev.platform_data; + struct bq24022_mach_info *pdata = rdev_get_drvdata(rdev); - dev_dbg(&pdev->dev, "setting current limit to %s mA\n", + dev_dbg(rdev_get_dev(rdev), "setting current limit to %s mA\n", max_uA >= 500000 ? "500" : "100"); /* REVISIT: maybe return error if min_uA != 0 ? */ @@ -34,18 +34,16 @@ static int bq24022_set_current_limit(struct regulator_dev *rdev, static int bq24022_get_current_limit(struct regulator_dev *rdev) { - struct platform_device *pdev = rdev_get_drvdata(rdev); - struct bq24022_mach_info *pdata = pdev->dev.platform_data; + struct bq24022_mach_info *pdata = rdev_get_drvdata(rdev); return gpio_get_value(pdata->gpio_iset2) ? 500000 : 100000; } static int bq24022_enable(struct regulator_dev *rdev) { - struct platform_device *pdev = rdev_get_drvdata(rdev); - struct bq24022_mach_info *pdata = pdev->dev.platform_data; + struct bq24022_mach_info *pdata = rdev_get_drvdata(rdev); - dev_dbg(&pdev->dev, "enabling charger\n"); + dev_dbg(rdev_get_dev(rdev), "enabling charger\n"); gpio_set_value(pdata->gpio_nce, 0); return 0; @@ -53,10 +51,9 @@ static int bq24022_enable(struct regulator_dev *rdev) static int bq24022_disable(struct regulator_dev *rdev) { - struct platform_device *pdev = rdev_get_drvdata(rdev); - struct bq24022_mach_info *pdata = pdev->dev.platform_data; + struct bq24022_mach_info *pdata = rdev_get_drvdata(rdev); - dev_dbg(&pdev->dev, "disabling charger\n"); + dev_dbg(rdev_get_dev(rdev), "disabling charger\n"); gpio_set_value(pdata->gpio_nce, 1); return 0; @@ -108,7 +105,7 @@ static int __init bq24022_probe(struct platform_device *pdev) ret = gpio_direction_output(pdata->gpio_iset2, 0); ret = gpio_direction_output(pdata->gpio_nce, 1); - bq24022 = regulator_register(&bq24022_desc, pdev); + bq24022 = regulator_register(&bq24022_desc, &pdev->dev, pdata); if (IS_ERR(bq24022)) { dev_dbg(&pdev->dev, "couldn't register regulator\n"); ret = PTR_ERR(bq24022); diff --git a/drivers/regulator/core.c b/drivers/regulator/core.c index 9c7986261568..02a774424e8d 100644 --- a/drivers/regulator/core.c +++ b/drivers/regulator/core.c @@ -2,8 +2,9 @@ * core.c -- Voltage/Current Regulator framework. * * Copyright 2007, 2008 Wolfson Microelectronics PLC. + * Copyright 2008 SlimLogic Ltd. * - * Author: Liam Girdwood <liam.girdwood@wolfsonmicro.com> + * Author: Liam Girdwood <lrg@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 as published by the @@ -64,14 +65,9 @@ struct regulator_map { struct list_head list; struct device *dev; const char *supply; - const char *regulator; + struct regulator_dev *regulator; }; -static inline struct regulator_dev *to_rdev(struct device *d) -{ - return container_of(d, struct regulator_dev, dev); -} - /* * struct regulator * @@ -227,7 +223,7 @@ static ssize_t device_requested_uA_show(struct device *dev, static ssize_t regulator_uV_show(struct device *dev, struct device_attribute *attr, char *buf) { - struct regulator_dev *rdev = to_rdev(dev); + struct regulator_dev *rdev = dev_get_drvdata(dev); ssize_t ret; mutex_lock(&rdev->mutex); @@ -240,15 +236,31 @@ static ssize_t regulator_uV_show(struct device *dev, static ssize_t regulator_uA_show(struct device *dev, struct device_attribute *attr, char *buf) { - struct regulator_dev *rdev = to_rdev(dev); + struct regulator_dev *rdev = dev_get_drvdata(dev); return sprintf(buf, "%d\n", _regulator_get_current_limit(rdev)); } +static ssize_t regulator_name_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct regulator_dev *rdev = dev_get_drvdata(dev); + const char *name; + + if (rdev->constraints->name) + name = rdev->constraints->name; + else if (rdev->desc->name) + name = rdev->desc->name; + else + name = ""; + + return sprintf(buf, "%s\n", name); +} + static ssize_t regulator_opmode_show(struct device *dev, struct device_attribute *attr, char *buf) { - struct regulator_dev *rdev = to_rdev(dev); + struct regulator_dev *rdev = dev_get_drvdata(dev); int mode = _regulator_get_mode(rdev); switch (mode) { @@ -267,7 +279,7 @@ static ssize_t regulator_opmode_show(struct device *dev, static ssize_t regulator_state_show(struct device *dev, struct device_attribute *attr, char *buf) { - struct regulator_dev *rdev = to_rdev(dev); + struct regulator_dev *rdev = dev_get_drvdata(dev); int state = _regulator_is_enabled(rdev); if (state > 0) @@ -281,7 +293,7 @@ static ssize_t regulator_state_show(struct device *dev, static ssize_t regulator_min_uA_show(struct device *dev, struct device_attribute *attr, char *buf) { - struct regulator_dev *rdev = to_rdev(dev); + struct regulator_dev *rdev = dev_get_drvdata(dev); if (!rdev->constraints) return sprintf(buf, "constraint not defined\n"); @@ -292,7 +304,7 @@ static ssize_t regulator_min_uA_show(struct device *dev, static ssize_t regulator_max_uA_show(struct device *dev, struct device_attribute *attr, char *buf) { - struct regulator_dev *rdev = to_rdev(dev); + struct regulator_dev *rdev = dev_get_drvdata(dev); if (!rdev->constraints) return sprintf(buf, "constraint not defined\n"); @@ -303,7 +315,7 @@ static ssize_t regulator_max_uA_show(struct device *dev, static ssize_t regulator_min_uV_show(struct device *dev, struct device_attribute *attr, char *buf) { - struct regulator_dev *rdev = to_rdev(dev); + struct regulator_dev *rdev = dev_get_drvdata(dev); if (!rdev->constraints) return sprintf(buf, "constraint not defined\n"); @@ -314,7 +326,7 @@ static ssize_t regulator_min_uV_show(struct device *dev, static ssize_t regulator_max_uV_show(struct device *dev, struct device_attribute *attr, char *buf) { - struct regulator_dev *rdev = to_rdev(dev); + struct regulator_dev *rdev = dev_get_drvdata(dev); if (!rdev->constraints) return sprintf(buf, "constraint not defined\n"); @@ -325,7 +337,7 @@ static ssize_t regulator_max_uV_show(struct device *dev, static ssize_t regulator_total_uA_show(struct device *dev, struct device_attribute *attr, char *buf) { - struct regulator_dev *rdev = to_rdev(dev); + struct regulator_dev *rdev = dev_get_drvdata(dev); struct regulator *regulator; int uA = 0; @@ -339,14 +351,14 @@ static ssize_t regulator_total_uA_show(struct device *dev, static ssize_t regulator_num_users_show(struct device *dev, struct device_attribute *attr, char *buf) { - struct regulator_dev *rdev = to_rdev(dev); + struct regulator_dev *rdev = dev_get_drvdata(dev); return sprintf(buf, "%d\n", rdev->use_count); } static ssize_t regulator_type_show(struct device *dev, struct device_attribute *attr, char *buf) { - struct regulator_dev *rdev = to_rdev(dev); + struct regulator_dev *rdev = dev_get_drvdata(dev); switch (rdev->desc->type) { case REGULATOR_VOLTAGE: @@ -360,7 +372,7 @@ static ssize_t regulator_type_show(struct device *dev, static ssize_t regulator_suspend_mem_uV_show(struct device *dev, struct device_attribute *attr, char *buf) { - struct regulator_dev *rdev = to_rdev(dev); + struct regulator_dev *rdev = dev_get_drvdata(dev); if (!rdev->constraints) return sprintf(buf, "not defined\n"); @@ -370,7 +382,7 @@ static ssize_t regulator_suspend_mem_uV_show(struct device *dev, static ssize_t regulator_suspend_disk_uV_show(struct device *dev, struct device_attribute *attr, char *buf) { - struct regulator_dev *rdev = to_rdev(dev); + struct regulator_dev *rdev = dev_get_drvdata(dev); if (!rdev->constraints) return sprintf(buf, "not defined\n"); @@ -380,7 +392,7 @@ static ssize_t regulator_suspend_disk_uV_show(struct device *dev, static ssize_t regulator_suspend_standby_uV_show(struct device *dev, struct device_attribute *attr, char *buf) { - struct regulator_dev *rdev = to_rdev(dev); + struct regulator_dev *rdev = dev_get_drvdata(dev); if (!rdev->constraints) return sprintf(buf, "not defined\n"); @@ -406,7 +418,7 @@ static ssize_t suspend_opmode_show(struct regulator_dev *rdev, static ssize_t regulator_suspend_mem_mode_show(struct device *dev, struct device_attribute *attr, char *buf) { - struct regulator_dev *rdev = to_rdev(dev); + struct regulator_dev *rdev = dev_get_drvdata(dev); if (!rdev->constraints) return sprintf(buf, "not defined\n"); @@ -417,7 +429,7 @@ static ssize_t regulator_suspend_mem_mode_show(struct device *dev, static ssize_t regulator_suspend_disk_mode_show(struct device *dev, struct device_attribute *attr, char *buf) { - struct regulator_dev *rdev = to_rdev(dev); + struct regulator_dev *rdev = dev_get_drvdata(dev); if (!rdev->constraints) return sprintf(buf, "not defined\n"); @@ -428,7 +440,7 @@ static ssize_t regulator_suspend_disk_mode_show(struct device *dev, static ssize_t regulator_suspend_standby_mode_show(struct device *dev, struct device_attribute *attr, char *buf) { - struct regulator_dev *rdev = to_rdev(dev); + struct regulator_dev *rdev = dev_get_drvdata(dev); if (!rdev->constraints) return sprintf(buf, "not defined\n"); @@ -439,7 +451,7 @@ static ssize_t regulator_suspend_standby_mode_show(struct device *dev, static ssize_t regulator_suspend_mem_state_show(struct device *dev, struct device_attribute *attr, char *buf) { - struct regulator_dev *rdev = to_rdev(dev); + struct regulator_dev *rdev = dev_get_drvdata(dev); if (!rdev->constraints) return sprintf(buf, "not defined\n"); @@ -453,7 +465,7 @@ static ssize_t regulator_suspend_mem_state_show(struct device *dev, static ssize_t regulator_suspend_disk_state_show(struct device *dev, struct device_attribute *attr, char *buf) { - struct regulator_dev *rdev = to_rdev(dev); + struct regulator_dev *rdev = dev_get_drvdata(dev); if (!rdev->constraints) return sprintf(buf, "not defined\n"); @@ -467,7 +479,7 @@ static ssize_t regulator_suspend_disk_state_show(struct device *dev, static ssize_t regulator_suspend_standby_state_show(struct device *dev, struct device_attribute *attr, char *buf) { - struct regulator_dev *rdev = to_rdev(dev); + struct regulator_dev *rdev = dev_get_drvdata(dev); if (!rdev->constraints) return sprintf(buf, "not defined\n"); @@ -477,7 +489,9 @@ static ssize_t regulator_suspend_standby_state_show(struct device *dev, else return sprintf(buf, "disabled\n"); } + static struct device_attribute regulator_dev_attrs[] = { + __ATTR(name, 0444, regulator_name_show, NULL), __ATTR(microvolts, 0444, regulator_uV_show, NULL), __ATTR(microamps, 0444, regulator_uA_show, NULL), __ATTR(opmode, 0444, regulator_opmode_show, NULL), @@ -512,7 +526,7 @@ static struct device_attribute regulator_dev_attrs[] = { static void regulator_dev_release(struct device *dev) { - struct regulator_dev *rdev = to_rdev(dev); + struct regulator_dev *rdev = dev_get_drvdata(dev); kfree(rdev); } @@ -569,8 +583,11 @@ static int suspend_set_state(struct regulator_dev *rdev, /* enable & disable are mandatory for suspend control */ if (!rdev->desc->ops->set_suspend_enable || - !rdev->desc->ops->set_suspend_disable) + !rdev->desc->ops->set_suspend_disable) { + printk(KERN_ERR "%s: no way to set suspend state\n", + __func__); return -EINVAL; + } if (rstate->enabled) ret = rdev->desc->ops->set_suspend_enable(rdev); @@ -656,6 +673,155 @@ static void print_constraints(struct regulator_dev *rdev) printk(KERN_INFO "regulator: %s: %s\n", rdev->desc->name, buf); } +/** + * set_machine_constraints - sets regulator constraints + * @regulator: regulator source + * + * Allows platform initialisation code to define and constrain + * regulator circuits e.g. valid voltage/current ranges, etc. NOTE: + * Constraints *must* be set by platform code in order for some + * regulator operations to proceed i.e. set_voltage, set_current_limit, + * set_mode. + */ +static int set_machine_constraints(struct regulator_dev *rdev, + struct regulation_constraints *constraints) +{ + int ret = 0; + const char *name; + struct regulator_ops *ops = rdev->desc->ops; + + if (constraints->name) + name = constraints->name; + else if (rdev->desc->name) + name = rdev->desc->name; + else + name = "regulator"; + + rdev->constraints = constraints; + + /* do we need to apply the constraint voltage */ + if (rdev->constraints->apply_uV && + rdev->constraints->min_uV == rdev->constraints->max_uV && + ops->set_voltage) { + ret = ops->set_voltage(rdev, + rdev->constraints->min_uV, rdev->constraints->max_uV); + if (ret < 0) { + printk(KERN_ERR "%s: failed to apply %duV constraint to %s\n", + __func__, + rdev->constraints->min_uV, name); + rdev->constraints = NULL; + goto out; + } + } + + /* are we enabled at boot time by firmware / bootloader */ + if (rdev->constraints->boot_on) + rdev->use_count = 1; + + /* do we need to setup our suspend state */ + if (constraints->initial_state) { + ret = suspend_prepare(rdev, constraints->initial_state); + if (ret < 0) { + printk(KERN_ERR "%s: failed to set suspend state for %s\n", + __func__, name); + rdev->constraints = NULL; + goto out; + } + } + + /* if always_on is set then turn the regulator on if it's not + * already on. */ + if (constraints->always_on && ops->enable && + ((ops->is_enabled && !ops->is_enabled(rdev)) || + (!ops->is_enabled && !constraints->boot_on))) { + ret = ops->enable(rdev); + if (ret < 0) { + printk(KERN_ERR "%s: failed to enable %s\n", + __func__, name); + rdev->constraints = NULL; + goto out; + } + } + + print_constraints(rdev); +out: + return ret; +} + +/** + * set_supply - set regulator supply regulator + * @regulator: regulator name + * @supply: supply regulator name + * + * Called by platform initialisation code to set the supply regulator for this + * regulator. This ensures that a regulators supply will also be enabled by the + * core if it's child is enabled. + */ +static int set_supply(struct regulator_dev *rdev, + struct regulator_dev *supply_rdev) +{ + int err; + + err = sysfs_create_link(&rdev->dev.kobj, &supply_rdev->dev.kobj, + "supply"); + if (err) { + printk(KERN_ERR + "%s: could not add device link %s err %d\n", + __func__, supply_rdev->dev.kobj.name, err); + goto out; + } + rdev->supply = supply_rdev; + list_add(&rdev->slist, &supply_rdev->supply_list); +out: + return err; +} + +/** + * set_consumer_device_supply: Bind a regulator to a symbolic supply + * @regulator: regulator source + * @dev: device the supply applies to + * @supply: symbolic name for supply + * + * Allows platform initialisation code to map physical regulator + * sources to symbolic names for supplies for use by devices. Devices + * should use these symbolic names to request regulators, avoiding the + * need to provide board-specific regulator names as platform data. + */ +static int set_consumer_device_supply(struct regulator_dev *rdev, + struct device *consumer_dev, const char *supply) +{ + struct regulator_map *node; + + if (supply == NULL) + return -EINVAL; + + node = kmalloc(sizeof(struct regulator_map), GFP_KERNEL); + if (node == NULL) + return -ENOMEM; + + node->regulator = rdev; + node->dev = consumer_dev; + node->supply = supply; + + list_add(&node->list, ®ulator_map_list); + return 0; +} + +static void unset_consumer_device_supply(struct regulator_dev *rdev, + struct device *consumer_dev) +{ + struct regulator_map *node, *n; + + list_for_each_entry_safe(node, n, ®ulator_map_list, list) { + if (rdev == node->regulator && + consumer_dev == node->dev) { + list_del(&node->list); + kfree(node); + return; + } + } +} + #define REG_STR_SIZE 32 static struct regulator *create_regulator(struct regulator_dev *rdev, @@ -746,7 +912,6 @@ struct regulator *regulator_get(struct device *dev, const char *id) struct regulator_dev *rdev; struct regulator_map *map; struct regulator *regulator = ERR_PTR(-ENODEV); - const char *supply = id; if (id == NULL) { printk(KERN_ERR "regulator: get() with no identifier\n"); @@ -758,15 +923,9 @@ struct regulator *regulator_get(struct device *dev, const char *id) list_for_each_entry(map, ®ulator_map_list, list) { if (dev == map->dev && strcmp(map->supply, id) == 0) { - supply = map->regulator; - break; - } - } - - list_for_each_entry(rdev, ®ulator_list, list) { - if (strcmp(supply, rdev->desc->name) == 0 && - try_module_get(rdev->owner)) + rdev = map->regulator; goto found; + } } printk(KERN_ERR "regulator: Unable to get requested regulator: %s\n", id); @@ -774,12 +933,16 @@ struct regulator *regulator_get(struct device *dev, const char *id) return regulator; found: + if (!try_module_get(rdev->owner)) + goto out; + regulator = create_regulator(rdev, dev, id); if (regulator == NULL) { regulator = ERR_PTR(-ENOMEM); module_put(rdev->owner); } +out: mutex_unlock(®ulator_list_mutex); return regulator; } @@ -1559,11 +1722,12 @@ EXPORT_SYMBOL_GPL(regulator_notifier_call_chain); * Returns 0 on success. */ struct regulator_dev *regulator_register(struct regulator_desc *regulator_desc, - void *reg_data) + struct device *dev, void *driver_data) { static atomic_t regulator_no = ATOMIC_INIT(0); struct regulator_dev *rdev; - int ret; + struct regulator_init_data *init_data = dev->platform_data; + int ret, i; if (regulator_desc == NULL) return ERR_PTR(-EINVAL); @@ -1575,6 +1739,9 @@ struct regulator_dev *regulator_register(struct regulator_desc *regulator_desc, !regulator_desc->type == REGULATOR_CURRENT) return ERR_PTR(-EINVAL); + if (!init_data) + return ERR_PTR(-EINVAL); + rdev = kzalloc(sizeof(struct regulator_dev), GFP_KERNEL); if (rdev == NULL) return ERR_PTR(-ENOMEM); @@ -1582,7 +1749,7 @@ struct regulator_dev *regulator_register(struct regulator_desc *regulator_desc, mutex_lock(®ulator_list_mutex); mutex_init(&rdev->mutex); - rdev->reg_data = reg_data; + rdev->reg_data = driver_data; rdev->owner = regulator_desc->owner; rdev->desc = regulator_desc; INIT_LIST_HEAD(&rdev->consumer_list); @@ -1591,20 +1758,68 @@ struct regulator_dev *regulator_register(struct regulator_desc *regulator_desc, INIT_LIST_HEAD(&rdev->slist); BLOCKING_INIT_NOTIFIER_HEAD(&rdev->notifier); + /* preform any regulator specific init */ + if (init_data->regulator_init) { + ret = init_data->regulator_init(rdev->reg_data); + if (ret < 0) { + kfree(rdev); + rdev = ERR_PTR(ret); + goto out; + } + } + + /* set regulator constraints */ + ret = set_machine_constraints(rdev, &init_data->constraints); + if (ret < 0) { + kfree(rdev); + rdev = ERR_PTR(ret); + goto out; + } + + /* register with sysfs */ rdev->dev.class = ®ulator_class; - device_initialize(&rdev->dev); + rdev->dev.parent = dev; snprintf(rdev->dev.bus_id, sizeof(rdev->dev.bus_id), - "regulator_%ld_%s", - (unsigned long)atomic_inc_return(®ulator_no) - 1, - regulator_desc->name); - - ret = device_add(&rdev->dev); - if (ret == 0) - list_add(&rdev->list, ®ulator_list); - else { + "regulator.%d", atomic_inc_return(®ulator_no) - 1); + ret = device_register(&rdev->dev); + if (ret != 0) { kfree(rdev); rdev = ERR_PTR(ret); + goto out; + } + + dev_set_drvdata(&rdev->dev, rdev); + + /* set supply regulator if it exists */ + if (init_data->supply_regulator_dev) { + ret = set_supply(rdev, + dev_get_drvdata(init_data->supply_regulator_dev)); + if (ret < 0) { + device_unregister(&rdev->dev); + kfree(rdev); + rdev = ERR_PTR(ret); + goto out; + } } + + /* add consumers devices */ + for (i = 0; i < init_data->num_consumer_supplies; i++) { + ret = set_consumer_device_supply(rdev, + init_data->consumer_supplies[i].dev, + init_data->consumer_supplies[i].supply); + if (ret < 0) { + for (--i; i >= 0; i--) + unset_consumer_device_supply(rdev, + init_data->consumer_supplies[i].dev); + device_unregister(&rdev->dev); + kfree(rdev); + rdev = ERR_PTR(ret); + goto out; + } + } + + list_add(&rdev->list, ®ulator_list); +out: mutex_unlock(®ulator_list_mutex); return rdev; } @@ -1631,187 +1846,6 @@ void regulator_unregister(struct regulator_dev *rdev) EXPORT_SYMBOL_GPL(regulator_unregister); /** - * regulator_set_supply - set regulator supply regulator - * @regulator: regulator name - * @supply: supply regulator name - * - * Called by platform initialisation code to set the supply regulator for this - * regulator. This ensures that a regulators supply will also be enabled by the - * core if it's child is enabled. - */ -int regulator_set_supply(const char *regulator, const char *supply) -{ - struct regulator_dev *rdev, *supply_rdev; - int err; - - if (regulator == NULL || supply == NULL) - return -EINVAL; - - mutex_lock(®ulator_list_mutex); - - list_for_each_entry(rdev, ®ulator_list, list) { - if (!strcmp(rdev->desc->name, regulator)) - goto found_regulator; - } - mutex_unlock(®ulator_list_mutex); - return -ENODEV; - -found_regulator: - list_for_each_entry(supply_rdev, ®ulator_list, list) { - if (!strcmp(supply_rdev->desc->name, supply)) - goto found_supply; - } - mutex_unlock(®ulator_list_mutex); - return -ENODEV; - -found_supply: - err = sysfs_create_link(&rdev->dev.kobj, &supply_rdev->dev.kobj, - "supply"); - if (err) { - printk(KERN_ERR - "%s: could not add device link %s err %d\n", - __func__, supply_rdev->dev.kobj.name, err); - goto out; - } - rdev->supply = supply_rdev; - list_add(&rdev->slist, &supply_rdev->supply_list); -out: - mutex_unlock(®ulator_list_mutex); - return err; -} -EXPORT_SYMBOL_GPL(regulator_set_supply); - -/** - * regulator_get_supply - get regulator supply regulator - * @regulator: regulator name - * - * Returns the supply supply regulator name or NULL if no supply regulator - * exists (i.e the regulator is supplied directly from USB, Line, Battery, etc) - */ -const char *regulator_get_supply(const char *regulator) -{ - struct regulator_dev *rdev; - - if (regulator == NULL) - return NULL; - - mutex_lock(®ulator_list_mutex); - list_for_each_entry(rdev, ®ulator_list, list) { - if (!strcmp(rdev->desc->name, regulator)) - goto found; - } - mutex_unlock(®ulator_list_mutex); - return NULL; - -found: - mutex_unlock(®ulator_list_mutex); - if (rdev->supply) - return rdev->supply->desc->name; - else - return NULL; -} -EXPORT_SYMBOL_GPL(regulator_get_supply); - -/** - * regulator_set_machine_constraints - sets regulator constraints - * @regulator: regulator source - * - * Allows platform initialisation code to define and constrain - * regulator circuits e.g. valid voltage/current ranges, etc. NOTE: - * Constraints *must* be set by platform code in order for some - * regulator operations to proceed i.e. set_voltage, set_current_limit, - * set_mode. - */ -int regulator_set_machine_constraints(const char *regulator_name, - struct regulation_constraints *constraints) -{ - struct regulator_dev *rdev; - int ret = 0; - - if (regulator_name == NULL) - return -EINVAL; - - mutex_lock(®ulator_list_mutex); - - list_for_each_entry(rdev, ®ulator_list, list) { - if (!strcmp(regulator_name, rdev->desc->name)) - goto found; - } - ret = -ENODEV; - goto out; - -found: - mutex_lock(&rdev->mutex); - rdev->constraints = constraints; - - /* do we need to apply the constraint voltage */ - if (rdev->constraints->apply_uV && - rdev->constraints->min_uV == rdev->constraints->max_uV && - rdev->desc->ops->set_voltage) { - ret = rdev->desc->ops->set_voltage(rdev, - rdev->constraints->min_uV, rdev->constraints->max_uV); - if (ret < 0) { - printk(KERN_ERR "%s: failed to apply %duV" - " constraint\n", __func__, - rdev->constraints->min_uV); - rdev->constraints = NULL; - goto out; - } - } - - /* are we enabled at boot time by firmware / bootloader */ - if (rdev->constraints->boot_on) - rdev->use_count = 1; - - /* do we need to setup our suspend state */ - if (constraints->initial_state) - ret = suspend_prepare(rdev, constraints->initial_state); - - print_constraints(rdev); - mutex_unlock(&rdev->mutex); - -out: - mutex_unlock(®ulator_list_mutex); - return ret; -} -EXPORT_SYMBOL_GPL(regulator_set_machine_constraints); - - -/** - * regulator_set_device_supply: Bind a regulator to a symbolic supply - * @regulator: regulator source - * @dev: device the supply applies to - * @supply: symbolic name for supply - * - * Allows platform initialisation code to map physical regulator - * sources to symbolic names for supplies for use by devices. Devices - * should use these symbolic names to request regulators, avoiding the - * need to provide board-specific regulator names as platform data. - */ -int regulator_set_device_supply(const char *regulator, struct device *dev, - const char *supply) -{ - struct regulator_map *node; - - if (regulator == NULL || supply == NULL) - return -EINVAL; - - node = kmalloc(sizeof(struct regulator_map), GFP_KERNEL); - if (node == NULL) - return -ENOMEM; - - node->regulator = regulator; - node->dev = dev; - node->supply = supply; - - mutex_lock(®ulator_list_mutex); - list_add(&node->list, ®ulator_map_list); - mutex_unlock(®ulator_list_mutex); - return 0; -} -EXPORT_SYMBOL_GPL(regulator_set_device_supply); - -/** * regulator_suspend_prepare: prepare regulators for system wide suspend * @state: system suspend state * @@ -1893,6 +1927,18 @@ int rdev_get_id(struct regulator_dev *rdev) } EXPORT_SYMBOL_GPL(rdev_get_id); +struct device *rdev_get_dev(struct regulator_dev *rdev) +{ + return &rdev->dev; +} +EXPORT_SYMBOL_GPL(rdev_get_dev); + +void *regulator_get_init_drvdata(struct regulator_init_data *reg_init_data) +{ + return reg_init_data->driver_data; +} +EXPORT_SYMBOL_GPL(regulator_get_init_drvdata); + static int __init regulator_init(void) { printk(KERN_INFO "regulator: core version %s\n", REGULATOR_VERSION); diff --git a/drivers/regulator/da903x.c b/drivers/regulator/da903x.c new file mode 100644 index 000000000000..3688e339db87 --- /dev/null +++ b/drivers/regulator/da903x.c @@ -0,0 +1,513 @@ +/* + * Regulators driver for Dialog Semiconductor DA903x + * + * Copyright (C) 2006-2008 Marvell International Ltd. + * Copyright (C) 2008 Compulab Ltd. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include <linux/kernel.h> +#include <linux/init.h> +#include <linux/err.h> +#include <linux/platform_device.h> +#include <linux/regulator/driver.h> +#include <linux/regulator/machine.h> +#include <linux/mfd/da903x.h> + +/* DA9030 Registers */ +#define DA9030_INVAL (-1) +#define DA9030_LDO1011 (0x10) +#define DA9030_LDO15 (0x11) +#define DA9030_LDO1416 (0x12) +#define DA9030_LDO1819 (0x13) +#define DA9030_LDO17 (0x14) +#define DA9030_BUCK2DVM1 (0x15) +#define DA9030_BUCK2DVM2 (0x16) +#define DA9030_RCTL11 (0x17) +#define DA9030_RCTL21 (0x18) +#define DA9030_LDO1 (0x90) +#define DA9030_LDO23 (0x91) +#define DA9030_LDO45 (0x92) +#define DA9030_LDO6 (0x93) +#define DA9030_LDO78 (0x94) +#define DA9030_LDO912 (0x95) +#define DA9030_BUCK (0x96) +#define DA9030_RCTL12 (0x97) +#define DA9030_RCTL22 (0x98) +#define DA9030_LDO_UNLOCK (0xa0) +#define DA9030_LDO_UNLOCK_MASK (0xe0) +#define DA9034_OVER1 (0x10) + +/* DA9034 Registers */ +#define DA9034_INVAL (-1) +#define DA9034_OVER2 (0x11) +#define DA9034_OVER3 (0x12) +#define DA9034_LDO643 (0x13) +#define DA9034_LDO987 (0x14) +#define DA9034_LDO1110 (0x15) +#define DA9034_LDO1312 (0x16) +#define DA9034_LDO1514 (0x17) +#define DA9034_VCC1 (0x20) +#define DA9034_ADTV1 (0x23) +#define DA9034_ADTV2 (0x24) +#define DA9034_AVRC (0x25) +#define DA9034_CDTV1 (0x26) +#define DA9034_CDTV2 (0x27) +#define DA9034_CVRC (0x28) +#define DA9034_SDTV1 (0x29) +#define DA9034_SDTV2 (0x2a) +#define DA9034_SVRC (0x2b) +#define DA9034_MDTV1 (0x32) +#define DA9034_MDTV2 (0x33) +#define DA9034_MVRC (0x34) + +struct da903x_regulator_info { + struct regulator_desc desc; + + int min_uV; + int max_uV; + int step_uV; + int vol_reg; + int vol_shift; + int vol_nbits; + int update_reg; + int update_bit; + int enable_reg; + int enable_bit; +}; + +static inline int check_range(struct da903x_regulator_info *info, + int min_uV, int max_uV) +{ + if (min_uV < info->min_uV || min_uV > info->max_uV) + return -EINVAL; + + return 0; +} + +/* DA9030/DA9034 common operations */ +static int da903x_set_ldo_voltage(struct regulator_dev *rdev, + int min_uV, int max_uV) +{ + struct da903x_regulator_info *info = rdev_get_drvdata(rdev); + struct device *da9034_dev = rdev_get_dev(rdev)->parent; + uint8_t val, mask; + + if (check_range(info, min_uV, max_uV)) { + pr_err("invalid voltage range (%d, %d) uV", min_uV, max_uV); + return -EINVAL; + } + + val = (min_uV - info->min_uV + info->step_uV - 1) / info->step_uV; + val <<= info->vol_shift; + mask = ((1 << info->vol_nbits) - 1) << info->vol_shift; + + return da903x_update(da9034_dev, info->vol_reg, val, mask); +} + +static int da903x_get_voltage(struct regulator_dev *rdev) +{ + struct da903x_regulator_info *info = rdev_get_drvdata(rdev); + struct device *da9034_dev = rdev_get_dev(rdev)->parent; + uint8_t val, mask; + int ret; + + ret = da903x_read(da9034_dev, info->vol_reg, &val); + if (ret) + return ret; + + mask = ((1 << info->vol_nbits) - 1) << info->vol_shift; + val = (val & mask) >> info->vol_shift; + + return info->min_uV + info->step_uV * val; +} + +static int da903x_enable(struct regulator_dev *rdev) +{ + struct da903x_regulator_info *info = rdev_get_drvdata(rdev); + struct device *da9034_dev = rdev_get_dev(rdev)->parent; + + return da903x_set_bits(da9034_dev, info->enable_reg, + 1 << info->enable_bit); +} + +static int da903x_disable(struct regulator_dev *rdev) +{ + struct da903x_regulator_info *info = rdev_get_drvdata(rdev); + struct device *da9034_dev = rdev_get_dev(rdev)->parent; + + return da903x_clr_bits(da9034_dev, info->enable_reg, + 1 << info->enable_bit); +} + +static int da903x_is_enabled(struct regulator_dev *rdev) +{ + struct da903x_regulator_info *info = rdev_get_drvdata(rdev); + struct device *da9034_dev = rdev_get_dev(rdev)->parent; + uint8_t reg_val; + int ret; + + ret = da903x_read(da9034_dev, info->enable_reg, ®_val); + if (ret) + return ret; + + return reg_val & (1 << info->enable_bit); +} + +/* DA9030 specific operations */ +static int da9030_set_ldo1_15_voltage(struct regulator_dev *rdev, + int min_uV, int max_uV) +{ + struct da903x_regulator_info *info = rdev_get_drvdata(rdev); + struct device *da903x_dev = rdev_get_dev(rdev)->parent; + uint8_t val, mask; + int ret; + + if (check_range(info, min_uV, max_uV)) { + pr_err("invalid voltage range (%d, %d) uV", min_uV, max_uV); + return -EINVAL; + } + + val = (min_uV - info->min_uV + info->step_uV - 1) / info->step_uV; + val <<= info->vol_shift; + mask = ((1 << info->vol_nbits) - 1) << info->vol_shift; + val |= DA9030_LDO_UNLOCK; /* have to set UNLOCK bits */ + mask |= DA9030_LDO_UNLOCK_MASK; + + /* write twice */ + ret = da903x_update(da903x_dev, info->vol_reg, val, mask); + if (ret) + return ret; + + return da903x_update(da903x_dev, info->vol_reg, val, mask); +} + +static int da9030_set_ldo14_voltage(struct regulator_dev *rdev, + int min_uV, int max_uV) +{ + struct da903x_regulator_info *info = rdev_get_drvdata(rdev); + struct device *da903x_dev = rdev_get_dev(rdev)->parent; + uint8_t val, mask; + int thresh; + + if (check_range(info, min_uV, max_uV)) { + pr_err("invalid voltage range (%d, %d) uV", min_uV, max_uV); + return -EINVAL; + } + + thresh = (info->max_uV + info->min_uV) / 2; + if (min_uV < thresh) { + val = (thresh - min_uV + info->step_uV - 1) / info->step_uV; + val |= 0x4; + } else { + val = (min_uV - thresh + info->step_uV - 1) / info->step_uV; + } + + val <<= info->vol_shift; + mask = ((1 << info->vol_nbits) - 1) << info->vol_shift; + + return da903x_update(da903x_dev, info->vol_reg, val, mask); +} + +static int da9030_get_ldo14_voltage(struct regulator_dev *rdev) +{ + struct da903x_regulator_info *info = rdev_get_drvdata(rdev); + struct device *da903x_dev = rdev_get_dev(rdev)->parent; + uint8_t val, mask; + int ret; + + ret = da903x_read(da903x_dev, info->vol_reg, &val); + if (ret) + return ret; + + mask = ((1 << info->vol_nbits) - 1) << info->vol_shift; + val = (val & mask) >> info->vol_shift; + + if (val & 0x4) + return info->min_uV + info->step_uV * (3 - (val & ~0x4)); + else + return (info->max_uV + info->min_uV) / 2 + + info->step_uV * (val & ~0x4); +} + +/* DA9034 specific operations */ +static int da9034_set_dvc_voltage(struct regulator_dev *rdev, + int min_uV, int max_uV) +{ + struct da903x_regulator_info *info = rdev_get_drvdata(rdev); + struct device *da9034_dev = rdev_get_dev(rdev)->parent; + uint8_t val, mask; + int ret; + + if (check_range(info, min_uV, max_uV)) { + pr_err("invalid voltage range (%d, %d) uV", min_uV, max_uV); + return -EINVAL; + } + + val = (min_uV - info->min_uV + info->step_uV - 1) / info->step_uV; + val <<= info->vol_shift; + mask = ((1 << info->vol_nbits) - 1) << info->vol_shift; + + ret = da903x_update(da9034_dev, info->vol_reg, val, mask); + if (ret) + return ret; + + ret = da903x_set_bits(da9034_dev, info->update_reg, + 1 << info->update_bit); + return ret; +} + +static int da9034_set_ldo12_voltage(struct regulator_dev *rdev, + int min_uV, int max_uV) +{ + struct da903x_regulator_info *info = rdev_get_drvdata(rdev); + struct device *da9034_dev = rdev_get_dev(rdev)->parent; + uint8_t val, mask; + + if (check_range(info, min_uV, max_uV)) { + pr_err("invalid voltage range (%d, %d) uV", min_uV, max_uV); + return -EINVAL; + } + + val = (min_uV - info->min_uV + info->step_uV - 1) / info->step_uV; + val = (val > 7 || val < 20) ? 8 : val - 12; + val <<= info->vol_shift; + mask = ((1 << info->vol_nbits) - 1) << info->vol_shift; + + return da903x_update(da9034_dev, info->vol_reg, val, mask); +} + +static int da9034_get_ldo12_voltage(struct regulator_dev *rdev) +{ + struct da903x_regulator_info *info = rdev_get_drvdata(rdev); + struct device *da9034_dev = rdev_get_dev(rdev)->parent; + uint8_t val, mask; + int ret; + + ret = da903x_read(da9034_dev, info->vol_reg, &val); + if (ret) + return ret; + + mask = ((1 << info->vol_nbits) - 1) << info->vol_shift; + val = (val & mask) >> info->vol_shift; + + if (val >= 8) + return 2700000 + info->step_uV * (val - 8); + + return info->min_uV + info->step_uV * val; +} + +static struct regulator_ops da903x_regulator_ldo_ops = { + .set_voltage = da903x_set_ldo_voltage, + .get_voltage = da903x_get_voltage, + .enable = da903x_enable, + .disable = da903x_disable, + .is_enabled = da903x_is_enabled, +}; + +/* NOTE: this is dedicated for the insane DA9030 LDO14 */ +static struct regulator_ops da9030_regulator_ldo14_ops = { + .set_voltage = da9030_set_ldo14_voltage, + .get_voltage = da9030_get_ldo14_voltage, + .enable = da903x_enable, + .disable = da903x_disable, + .is_enabled = da903x_is_enabled, +}; + +/* NOTE: this is dedicated for the DA9030 LDO1 and LDO15 that have locks */ +static struct regulator_ops da9030_regulator_ldo1_15_ops = { + .set_voltage = da9030_set_ldo1_15_voltage, + .get_voltage = da903x_get_voltage, + .enable = da903x_enable, + .disable = da903x_disable, + .is_enabled = da903x_is_enabled, +}; + +static struct regulator_ops da9034_regulator_dvc_ops = { + .set_voltage = da9034_set_dvc_voltage, + .get_voltage = da903x_get_voltage, + .enable = da903x_enable, + .disable = da903x_disable, + .is_enabled = da903x_is_enabled, +}; + +/* NOTE: this is dedicated for the insane LDO12 */ +static struct regulator_ops da9034_regulator_ldo12_ops = { + .set_voltage = da9034_set_ldo12_voltage, + .get_voltage = da9034_get_ldo12_voltage, + .enable = da903x_enable, + .disable = da903x_disable, + .is_enabled = da903x_is_enabled, +}; + +#define DA903x_LDO(_pmic, _id, min, max, step, vreg, shift, nbits, ereg, ebit) \ +{ \ + .desc = { \ + .name = "LDO" #_id, \ + .ops = &da903x_regulator_ldo_ops, \ + .type = REGULATOR_VOLTAGE, \ + .id = _pmic##_ID_LDO##_id, \ + .owner = THIS_MODULE, \ + }, \ + .min_uV = (min) * 1000, \ + .max_uV = (max) * 1000, \ + .step_uV = (step) * 1000, \ + .vol_reg = _pmic##_##vreg, \ + .vol_shift = (shift), \ + .vol_nbits = (nbits), \ + .enable_reg = _pmic##_##ereg, \ + .enable_bit = (ebit), \ +} + +#define DA9034_DVC(_id, min, max, step, vreg, nbits, ureg, ubit, ereg, ebit) \ +{ \ + .desc = { \ + .name = #_id, \ + .ops = &da9034_regulator_dvc_ops, \ + .type = REGULATOR_VOLTAGE, \ + .id = DA9034_ID_##_id, \ + .owner = THIS_MODULE, \ + }, \ + .min_uV = (min) * 1000, \ + .max_uV = (max) * 1000, \ + .step_uV = (step) * 1000, \ + .vol_reg = DA9034_##vreg, \ + .vol_shift = (0), \ + .vol_nbits = (nbits), \ + .update_reg = DA9034_##ureg, \ + .update_bit = (ubit), \ + .enable_reg = DA9034_##ereg, \ + .enable_bit = (ebit), \ +} + +#define DA9034_LDO(_id, min, max, step, vreg, shift, nbits, ereg, ebit) \ + DA903x_LDO(DA9034, _id, min, max, step, vreg, shift, nbits, ereg, ebit) + +#define DA9030_LDO(_id, min, max, step, vreg, shift, nbits, ereg, ebit) \ + DA903x_LDO(DA9030, _id, min, max, step, vreg, shift, nbits, ereg, ebit) + +static struct da903x_regulator_info da903x_regulator_info[] = { + /* DA9030 */ + DA9030_LDO( 1, 1200, 3200, 100, LDO1, 0, 5, RCTL12, 1), + DA9030_LDO( 2, 1800, 3200, 100, LDO23, 0, 4, RCTL12, 2), + DA9030_LDO( 3, 1800, 3200, 100, LDO23, 4, 4, RCTL12, 3), + DA9030_LDO( 4, 1800, 3200, 100, LDO45, 0, 4, RCTL12, 4), + DA9030_LDO( 5, 1800, 3200, 100, LDO45, 4, 4, RCTL12, 5), + DA9030_LDO( 6, 1800, 3200, 100, LDO6, 0, 4, RCTL12, 6), + DA9030_LDO( 7, 1800, 3200, 100, LDO78, 0, 4, RCTL12, 7), + DA9030_LDO( 8, 1800, 3200, 100, LDO78, 4, 4, RCTL22, 0), + DA9030_LDO( 9, 1800, 3200, 100, LDO912, 0, 4, RCTL22, 1), + DA9030_LDO(10, 1800, 3200, 100, LDO1011, 0, 4, RCTL22, 2), + DA9030_LDO(11, 1800, 3200, 100, LDO1011, 4, 4, RCTL22, 3), + DA9030_LDO(12, 1800, 3200, 100, LDO912, 4, 4, RCTL22, 4), + DA9030_LDO(14, 2760, 2940, 30, LDO1416, 0, 3, RCTL11, 4), + DA9030_LDO(15, 1100, 2650, 50, LDO15, 0, 5, RCTL11, 5), + DA9030_LDO(16, 1100, 2650, 50, LDO1416, 3, 5, RCTL11, 6), + DA9030_LDO(17, 1800, 3200, 100, LDO17, 0, 4, RCTL11, 7), + DA9030_LDO(18, 1800, 3200, 100, LDO1819, 0, 4, RCTL21, 2), + DA9030_LDO(19, 1800, 3200, 100, LDO1819, 4, 4, RCTL21, 1), + DA9030_LDO(13, 2100, 2100, 0, INVAL, 0, 0, RCTL11, 3), /* fixed @2.1V */ + + /* DA9034 */ + DA9034_DVC(BUCK1, 725, 1500, 25, ADTV1, 5, VCC1, 0, OVER1, 0), + DA9034_DVC(BUCK2, 725, 1500, 25, CDTV1, 5, VCC1, 2, OVER1, 1), + DA9034_DVC(LDO2, 725, 1500, 25, SDTV1, 5, VCC1, 4, OVER1, 2), + DA9034_DVC(LDO1, 1700, 2075, 25, MDTV1, 4, VCC1, 6, OVER3, 4), + + DA9034_LDO( 3, 1800, 3300, 100, LDO643, 0, 4, OVER3, 5), + DA9034_LDO( 4, 1800, 2900,1100, LDO643, 4, 1, OVER3, 6), + DA9034_LDO( 6, 2500, 2850, 50, LDO643, 5, 3, OVER2, 0), + DA9034_LDO( 7, 2700, 3050, 50, LDO987, 0, 3, OVER2, 1), + DA9034_LDO( 8, 2700, 2850, 50, LDO987, 3, 2, OVER2, 2), + DA9034_LDO( 9, 2700, 3050, 50, LDO987, 5, 3, OVER2, 3), + DA9034_LDO(10, 2700, 3050, 50, LDO1110, 0, 3, OVER2, 4), + DA9034_LDO(11, 1800, 3300, 100, LDO1110, 4, 4, OVER2, 5), + DA9034_LDO(12, 1700, 3050, 50, LDO1312, 0, 4, OVER3, 6), + DA9034_LDO(13, 1800, 3300, 100, LDO1312, 4, 4, OVER2, 7), + DA9034_LDO(14, 1800, 3300, 100, LDO1514, 0, 4, OVER3, 0), + DA9034_LDO(15, 1800, 3300, 100, LDO1514, 4, 4, OVER3, 1), + DA9034_LDO(5, 3100, 3100, 0, INVAL, 0, 0, OVER3, 7), /* fixed @3.1V */ +}; + +static inline struct da903x_regulator_info *find_regulator_info(int id) +{ + struct da903x_regulator_info *ri; + int i; + + for (i = 0; i < ARRAY_SIZE(da903x_regulator_info); i++) { + ri = &da903x_regulator_info[i]; + if (ri->desc.id == id) + return ri; + } + return NULL; +} + +static int __devinit da903x_regulator_probe(struct platform_device *pdev) +{ + struct da903x_regulator_info *ri = NULL; + struct regulator_dev *rdev; + + ri = find_regulator_info(pdev->id); + if (ri == NULL) { + dev_err(&pdev->dev, "invalid regulator ID specified\n"); + return -EINVAL; + } + + /* Workaround for the weird LDO12 voltage setting */ + if (ri->desc.id == DA9034_ID_LDO12) + ri->desc.ops = &da9034_regulator_ldo12_ops; + + if (ri->desc.id == DA9030_ID_LDO14) + ri->desc.ops = &da9030_regulator_ldo14_ops; + + if (ri->desc.id == DA9030_ID_LDO1 || ri->desc.id == DA9030_ID_LDO15) + ri->desc.ops = &da9030_regulator_ldo1_15_ops; + + rdev = regulator_register(&ri->desc, pdev->dev.parent, ri); + if (IS_ERR(rdev)) { + dev_err(&pdev->dev, "failed to register regulator %s\n", + ri->desc.name); + return PTR_ERR(rdev); + } + + platform_set_drvdata(pdev, rdev); + return 0; +} + +static int __devexit da903x_regulator_remove(struct platform_device *pdev) +{ + struct regulator_dev *rdev = platform_get_drvdata(pdev); + + regulator_unregister(rdev); + return 0; +} + +static struct platform_driver da903x_regulator_driver = { + .driver = { + .name = "da903x-regulator", + .owner = THIS_MODULE, + }, + .probe = da903x_regulator_probe, + .remove = da903x_regulator_remove, +}; + +static int __init da903x_regulator_init(void) +{ + return platform_driver_register(&da903x_regulator_driver); +} +module_init(da903x_regulator_init); + +static void __exit da903x_regulator_exit(void) +{ + platform_driver_unregister(&da903x_regulator_driver); +} +module_exit(da903x_regulator_exit); + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Eric Miao <eric.miao@marvell.com>" + "Mike Rapoport <mike@compulab.co.il>"); +MODULE_DESCRIPTION("Regulator Driver for Dialog Semiconductor DA903X PMIC"); +MODULE_ALIAS("platform:da903x-regulator"); diff --git a/drivers/regulator/wm8350-regulator.c b/drivers/regulator/wm8350-regulator.c new file mode 100644 index 000000000000..1f44b17e23b1 --- /dev/null +++ b/drivers/regulator/wm8350-regulator.c @@ -0,0 +1,1431 @@ +/* + * wm8350.c -- Voltage and current regulation for the Wolfson WM8350 PMIC + * + * Copyright 2007, 2008 Wolfson Microelectronics PLC. + * + * Author: Liam Girdwood + * linux@wolfsonmicro.com + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + */ + +#include <linux/module.h> +#include <linux/moduleparam.h> +#include <linux/init.h> +#include <linux/bitops.h> +#include <linux/err.h> +#include <linux/i2c.h> +#include <linux/mfd/wm8350/core.h> +#include <linux/mfd/wm8350/pmic.h> +#include <linux/platform_device.h> +#include <linux/regulator/driver.h> +#include <linux/regulator/machine.h> + +/* Microamps */ +static const int isink_cur[] = { + 4, + 5, + 6, + 7, + 8, + 10, + 11, + 14, + 16, + 19, + 23, + 27, + 32, + 39, + 46, + 54, + 65, + 77, + 92, + 109, + 130, + 154, + 183, + 218, + 259, + 308, + 367, + 436, + 518, + 616, + 733, + 872, + 1037, + 1233, + 1466, + 1744, + 2073, + 2466, + 2933, + 3487, + 4147, + 4932, + 5865, + 6975, + 8294, + 9864, + 11730, + 13949, + 16589, + 19728, + 23460, + 27899, + 33178, + 39455, + 46920, + 55798, + 66355, + 78910, + 93840, + 111596, + 132710, + 157820, + 187681, + 223191 +}; + +static int get_isink_val(int min_uA, int max_uA, u16 *setting) +{ + int i; + + for (i = ARRAY_SIZE(isink_cur) - 1; i >= 0; i--) { + if (min_uA <= isink_cur[i] && max_uA >= isink_cur[i]) { + *setting = i; + return 0; + } + } + return -EINVAL; +} + +static inline int wm8350_ldo_val_to_mvolts(unsigned int val) +{ + if (val < 16) + return (val * 50) + 900; + else + return ((val - 16) * 100) + 1800; + +} + +static inline unsigned int wm8350_ldo_mvolts_to_val(int mV) +{ + if (mV < 1800) + return (mV - 900) / 50; + else + return ((mV - 1800) / 100) + 16; +} + +static inline int wm8350_dcdc_val_to_mvolts(unsigned int val) +{ + return (val * 25) + 850; +} + +static inline unsigned int wm8350_dcdc_mvolts_to_val(int mV) +{ + return (mV - 850) / 25; +} + +static int wm8350_isink_set_current(struct regulator_dev *rdev, int min_uA, + int max_uA) +{ + struct wm8350 *wm8350 = rdev_get_drvdata(rdev); + int isink = rdev_get_id(rdev); + u16 val, setting; + int ret; + + ret = get_isink_val(min_uA, max_uA, &setting); + if (ret != 0) + return ret; + + switch (isink) { + case WM8350_ISINK_A: + val = wm8350_reg_read(wm8350, WM8350_CURRENT_SINK_DRIVER_A) & + ~WM8350_CS1_ISEL_MASK; + wm8350_reg_write(wm8350, WM8350_CURRENT_SINK_DRIVER_A, + val | setting); + break; + case WM8350_ISINK_B: + val = wm8350_reg_read(wm8350, WM8350_CURRENT_SINK_DRIVER_B) & + ~WM8350_CS1_ISEL_MASK; + wm8350_reg_write(wm8350, WM8350_CURRENT_SINK_DRIVER_B, + val | setting); + break; + default: + return -EINVAL; + } + + return 0; +} + +static int wm8350_isink_get_current(struct regulator_dev *rdev) +{ + struct wm8350 *wm8350 = rdev_get_drvdata(rdev); + int isink = rdev_get_id(rdev); + u16 val; + + switch (isink) { + case WM8350_ISINK_A: + val = wm8350_reg_read(wm8350, WM8350_CURRENT_SINK_DRIVER_A) & + WM8350_CS1_ISEL_MASK; + break; + case WM8350_ISINK_B: + val = wm8350_reg_read(wm8350, WM8350_CURRENT_SINK_DRIVER_B) & + WM8350_CS1_ISEL_MASK; + break; + default: + return 0; + } + + return (isink_cur[val] + 50) / 100; +} + +/* turn on ISINK followed by DCDC */ +static int wm8350_isink_enable(struct regulator_dev *rdev) +{ + struct wm8350 *wm8350 = rdev_get_drvdata(rdev); + int isink = rdev_get_id(rdev); + + switch (isink) { + case WM8350_ISINK_A: + switch (wm8350->pmic.isink_A_dcdc) { + case WM8350_DCDC_2: + case WM8350_DCDC_5: + wm8350_set_bits(wm8350, WM8350_POWER_MGMT_7, + WM8350_CS1_ENA); + wm8350_set_bits(wm8350, WM8350_CSA_FLASH_CONTROL, + WM8350_CS1_DRIVE); + wm8350_set_bits(wm8350, WM8350_DCDC_LDO_REQUESTED, + 1 << (wm8350->pmic.isink_A_dcdc - + WM8350_DCDC_1)); + break; + default: + return -EINVAL; + } + break; + case WM8350_ISINK_B: + switch (wm8350->pmic.isink_B_dcdc) { + case WM8350_DCDC_2: + case WM8350_DCDC_5: + wm8350_set_bits(wm8350, WM8350_POWER_MGMT_7, + WM8350_CS2_ENA); + wm8350_set_bits(wm8350, WM8350_CSB_FLASH_CONTROL, + WM8350_CS2_DRIVE); + wm8350_set_bits(wm8350, WM8350_DCDC_LDO_REQUESTED, + 1 << (wm8350->pmic.isink_B_dcdc - + WM8350_DCDC_1)); + break; + default: + return -EINVAL; + } + break; + default: + return -EINVAL; + } + return 0; +} + +static int wm8350_isink_disable(struct regulator_dev *rdev) +{ + struct wm8350 *wm8350 = rdev_get_drvdata(rdev); + int isink = rdev_get_id(rdev); + + switch (isink) { + case WM8350_ISINK_A: + switch (wm8350->pmic.isink_A_dcdc) { + case WM8350_DCDC_2: + case WM8350_DCDC_5: + wm8350_clear_bits(wm8350, WM8350_DCDC_LDO_REQUESTED, + 1 << (wm8350->pmic.isink_A_dcdc - + WM8350_DCDC_1)); + wm8350_clear_bits(wm8350, WM8350_POWER_MGMT_7, + WM8350_CS1_ENA); + break; + default: + return -EINVAL; + } + break; + case WM8350_ISINK_B: + switch (wm8350->pmic.isink_B_dcdc) { + case WM8350_DCDC_2: + case WM8350_DCDC_5: + wm8350_clear_bits(wm8350, WM8350_DCDC_LDO_REQUESTED, + 1 << (wm8350->pmic.isink_B_dcdc - + WM8350_DCDC_1)); + wm8350_clear_bits(wm8350, WM8350_POWER_MGMT_7, + WM8350_CS2_ENA); + break; + default: + return -EINVAL; + } + break; + default: + return -EINVAL; + } + return 0; +} + +static int wm8350_isink_is_enabled(struct regulator_dev *rdev) +{ + struct wm8350 *wm8350 = rdev_get_drvdata(rdev); + int isink = rdev_get_id(rdev); + + switch (isink) { + case WM8350_ISINK_A: + return wm8350_reg_read(wm8350, WM8350_CURRENT_SINK_DRIVER_A) & + 0x8000; + case WM8350_ISINK_B: + return wm8350_reg_read(wm8350, WM8350_CURRENT_SINK_DRIVER_B) & + 0x8000; + } + return -EINVAL; +} + +int wm8350_isink_set_flash(struct wm8350 *wm8350, int isink, u16 mode, + u16 trigger, u16 duration, u16 on_ramp, u16 off_ramp, + u16 drive) +{ + switch (isink) { + case WM8350_ISINK_A: + wm8350_reg_write(wm8350, WM8350_CSA_FLASH_CONTROL, + (mode ? WM8350_CS1_FLASH_MODE : 0) | + (trigger ? WM8350_CS1_TRIGSRC : 0) | + duration | on_ramp | off_ramp | drive); + break; + case WM8350_ISINK_B: + wm8350_reg_write(wm8350, WM8350_CSB_FLASH_CONTROL, + (mode ? WM8350_CS2_FLASH_MODE : 0) | + (trigger ? WM8350_CS2_TRIGSRC : 0) | + duration | on_ramp | off_ramp | drive); + break; + default: + return -EINVAL; + } + return 0; +} +EXPORT_SYMBOL_GPL(wm8350_isink_set_flash); + +static int wm8350_dcdc_set_voltage(struct regulator_dev *rdev, int min_uV, + int max_uV) +{ + struct wm8350 *wm8350 = rdev_get_drvdata(rdev); + int volt_reg, dcdc = rdev_get_id(rdev), mV, + min_mV = min_uV / 1000, max_mV = max_uV / 1000; + u16 val; + + if (min_mV < 850 || min_mV > 4025) + return -EINVAL; + if (max_mV < 850 || max_mV > 4025) + return -EINVAL; + + /* step size is 25mV */ + mV = (min_mV - 826) / 25; + if (wm8350_dcdc_val_to_mvolts(mV) > max_mV) + return -EINVAL; + BUG_ON(wm8350_dcdc_val_to_mvolts(mV) < min_mV); + + switch (dcdc) { + case WM8350_DCDC_1: + volt_reg = WM8350_DCDC1_CONTROL; + break; + case WM8350_DCDC_3: + volt_reg = WM8350_DCDC3_CONTROL; + break; + case WM8350_DCDC_4: + volt_reg = WM8350_DCDC4_CONTROL; + break; + case WM8350_DCDC_6: + volt_reg = WM8350_DCDC6_CONTROL; + break; + case WM8350_DCDC_2: + case WM8350_DCDC_5: + default: + return -EINVAL; + } + + /* all DCDCs have same mV bits */ + val = wm8350_reg_read(wm8350, volt_reg) & ~WM8350_DC1_VSEL_MASK; + wm8350_reg_write(wm8350, volt_reg, val | mV); + return 0; +} + +static int wm8350_dcdc_get_voltage(struct regulator_dev *rdev) +{ + struct wm8350 *wm8350 = rdev_get_drvdata(rdev); + int volt_reg, dcdc = rdev_get_id(rdev); + u16 val; + + switch (dcdc) { + case WM8350_DCDC_1: + volt_reg = WM8350_DCDC1_CONTROL; + break; + case WM8350_DCDC_3: + volt_reg = WM8350_DCDC3_CONTROL; + break; + case WM8350_DCDC_4: + volt_reg = WM8350_DCDC4_CONTROL; + break; + case WM8350_DCDC_6: + volt_reg = WM8350_DCDC6_CONTROL; + break; + case WM8350_DCDC_2: + case WM8350_DCDC_5: + default: + return -EINVAL; + } + + /* all DCDCs have same mV bits */ + val = wm8350_reg_read(wm8350, volt_reg) & WM8350_DC1_VSEL_MASK; + return wm8350_dcdc_val_to_mvolts(val) * 1000; +} + +static int wm8350_dcdc_set_suspend_voltage(struct regulator_dev *rdev, int uV) +{ + struct wm8350 *wm8350 = rdev_get_drvdata(rdev); + int volt_reg, mV = uV / 1000, dcdc = rdev_get_id(rdev); + u16 val; + + dev_dbg(wm8350->dev, "%s %d mV %d\n", __func__, dcdc, mV); + + if (mV && (mV < 850 || mV > 4025)) { + dev_err(wm8350->dev, + "DCDC%d suspend voltage %d mV out of range\n", + dcdc, mV); + return -EINVAL; + } + if (mV == 0) + mV = 850; + + switch (dcdc) { + case WM8350_DCDC_1: + volt_reg = WM8350_DCDC1_LOW_POWER; + break; + case WM8350_DCDC_3: + volt_reg = WM8350_DCDC3_LOW_POWER; + break; + case WM8350_DCDC_4: + volt_reg = WM8350_DCDC4_LOW_POWER; + break; + case WM8350_DCDC_6: + volt_reg = WM8350_DCDC6_LOW_POWER; + break; + case WM8350_DCDC_2: + case WM8350_DCDC_5: + default: + return -EINVAL; + } + + /* all DCDCs have same mV bits */ + val = wm8350_reg_read(wm8350, volt_reg) & ~WM8350_DC1_VSEL_MASK; + wm8350_reg_write(wm8350, volt_reg, + val | wm8350_dcdc_mvolts_to_val(mV)); + return 0; +} + +static int wm8350_dcdc_set_suspend_enable(struct regulator_dev *rdev) +{ + struct wm8350 *wm8350 = rdev_get_drvdata(rdev); + int dcdc = rdev_get_id(rdev); + u16 val; + + switch (dcdc) { + case WM8350_DCDC_1: + val = wm8350_reg_read(wm8350, WM8350_DCDC1_LOW_POWER) + & ~WM8350_DCDC_HIB_MODE_MASK; + wm8350_reg_write(wm8350, WM8350_DCDC1_LOW_POWER, + wm8350->pmic.dcdc1_hib_mode); + break; + case WM8350_DCDC_3: + val = wm8350_reg_read(wm8350, WM8350_DCDC3_LOW_POWER) + & ~WM8350_DCDC_HIB_MODE_MASK; + wm8350_reg_write(wm8350, WM8350_DCDC3_LOW_POWER, + wm8350->pmic.dcdc3_hib_mode); + break; + case WM8350_DCDC_4: + val = wm8350_reg_read(wm8350, WM8350_DCDC4_LOW_POWER) + & ~WM8350_DCDC_HIB_MODE_MASK; + wm8350_reg_write(wm8350, WM8350_DCDC4_LOW_POWER, + wm8350->pmic.dcdc4_hib_mode); + break; + case WM8350_DCDC_6: + val = wm8350_reg_read(wm8350, WM8350_DCDC6_LOW_POWER) + & ~WM8350_DCDC_HIB_MODE_MASK; + wm8350_reg_write(wm8350, WM8350_DCDC6_LOW_POWER, + wm8350->pmic.dcdc6_hib_mode); + break; + case WM8350_DCDC_2: + case WM8350_DCDC_5: + default: + return -EINVAL; + } + + return 0; +} + +static int wm8350_dcdc_set_suspend_disable(struct regulator_dev *rdev) +{ + struct wm8350 *wm8350 = rdev_get_drvdata(rdev); + int dcdc = rdev_get_id(rdev); + u16 val; + + switch (dcdc) { + case WM8350_DCDC_1: + val = wm8350_reg_read(wm8350, WM8350_DCDC1_LOW_POWER); + wm8350->pmic.dcdc1_hib_mode = val & WM8350_DCDC_HIB_MODE_MASK; + wm8350_reg_write(wm8350, WM8350_DCDC1_LOW_POWER, + WM8350_DCDC_HIB_MODE_DIS); + break; + case WM8350_DCDC_3: + val = wm8350_reg_read(wm8350, WM8350_DCDC3_LOW_POWER); + wm8350->pmic.dcdc3_hib_mode = val & WM8350_DCDC_HIB_MODE_MASK; + wm8350_reg_write(wm8350, WM8350_DCDC3_LOW_POWER, + WM8350_DCDC_HIB_MODE_DIS); + break; + case WM8350_DCDC_4: + val = wm8350_reg_read(wm8350, WM8350_DCDC4_LOW_POWER); + wm8350->pmic.dcdc4_hib_mode = val & WM8350_DCDC_HIB_MODE_MASK; + wm8350_reg_write(wm8350, WM8350_DCDC4_LOW_POWER, + WM8350_DCDC_HIB_MODE_DIS); + break; + case WM8350_DCDC_6: + val = wm8350_reg_read(wm8350, WM8350_DCDC6_LOW_POWER); + wm8350->pmic.dcdc6_hib_mode = val & WM8350_DCDC_HIB_MODE_MASK; + wm8350_reg_write(wm8350, WM8350_DCDC6_LOW_POWER, + WM8350_DCDC_HIB_MODE_DIS); + break; + case WM8350_DCDC_2: + case WM8350_DCDC_5: + default: + return -EINVAL; + } + + return 0; +} + +static int wm8350_dcdc25_set_suspend_enable(struct regulator_dev *rdev) +{ + struct wm8350 *wm8350 = rdev_get_drvdata(rdev); + int dcdc = rdev_get_id(rdev); + u16 val; + + switch (dcdc) { + case WM8350_DCDC_2: + val = wm8350_reg_read(wm8350, WM8350_DCDC2_CONTROL) + & ~WM8350_DC2_HIB_MODE_MASK; + wm8350_reg_write(wm8350, WM8350_DCDC2_CONTROL, val | + WM8350_DC2_HIB_MODE_ACTIVE); + break; + case WM8350_DCDC_5: + val = wm8350_reg_read(wm8350, WM8350_DCDC5_CONTROL) + & ~WM8350_DC2_HIB_MODE_MASK; + wm8350_reg_write(wm8350, WM8350_DCDC5_CONTROL, val | + WM8350_DC5_HIB_MODE_ACTIVE); + break; + default: + return -EINVAL; + } + return 0; +} + +static int wm8350_dcdc25_set_suspend_disable(struct regulator_dev *rdev) +{ + struct wm8350 *wm8350 = rdev_get_drvdata(rdev); + int dcdc = rdev_get_id(rdev); + u16 val; + + switch (dcdc) { + case WM8350_DCDC_2: + val = wm8350_reg_read(wm8350, WM8350_DCDC2_CONTROL) + & ~WM8350_DC2_HIB_MODE_MASK; + wm8350_reg_write(wm8350, WM8350_DCDC2_CONTROL, val | + WM8350_DC2_HIB_MODE_DISABLE); + break; + case WM8350_DCDC_5: + val = wm8350_reg_read(wm8350, WM8350_DCDC5_CONTROL) + & ~WM8350_DC2_HIB_MODE_MASK; + wm8350_reg_write(wm8350, WM8350_DCDC5_CONTROL, val | + WM8350_DC2_HIB_MODE_DISABLE); + break; + default: + return -EINVAL; + } + return 0; +} + +static int wm8350_dcdc_set_suspend_mode(struct regulator_dev *rdev, + unsigned int mode) +{ + struct wm8350 *wm8350 = rdev_get_drvdata(rdev); + int dcdc = rdev_get_id(rdev); + u16 *hib_mode; + + switch (dcdc) { + case WM8350_DCDC_1: + hib_mode = &wm8350->pmic.dcdc1_hib_mode; + break; + case WM8350_DCDC_3: + hib_mode = &wm8350->pmic.dcdc3_hib_mode; + break; + case WM8350_DCDC_4: + hib_mode = &wm8350->pmic.dcdc4_hib_mode; + break; + case WM8350_DCDC_6: + hib_mode = &wm8350->pmic.dcdc6_hib_mode; + break; + case WM8350_DCDC_2: + case WM8350_DCDC_5: + default: + return -EINVAL; + } + + switch (mode) { + case REGULATOR_MODE_NORMAL: + *hib_mode = WM8350_DCDC_HIB_MODE_IMAGE; + break; + case REGULATOR_MODE_IDLE: + *hib_mode = WM8350_DCDC_HIB_MODE_STANDBY; + break; + case REGULATOR_MODE_STANDBY: + *hib_mode = WM8350_DCDC_HIB_MODE_LDO_IM; + break; + default: + return -EINVAL; + } + + return 0; +} + +static int wm8350_ldo_set_suspend_voltage(struct regulator_dev *rdev, int uV) +{ + struct wm8350 *wm8350 = rdev_get_drvdata(rdev); + int volt_reg, mV = uV / 1000, ldo = rdev_get_id(rdev); + u16 val; + + dev_dbg(wm8350->dev, "%s %d mV %d\n", __func__, ldo, mV); + + if (mV < 900 || mV > 3300) { + dev_err(wm8350->dev, "LDO%d voltage %d mV out of range\n", + ldo, mV); + return -EINVAL; + } + + switch (ldo) { + case WM8350_LDO_1: + volt_reg = WM8350_LDO1_LOW_POWER; + break; + case WM8350_LDO_2: + volt_reg = WM8350_LDO2_LOW_POWER; + break; + case WM8350_LDO_3: + volt_reg = WM8350_LDO3_LOW_POWER; + break; + case WM8350_LDO_4: + volt_reg = WM8350_LDO4_LOW_POWER; + break; + default: + return -EINVAL; + } + + /* all LDOs have same mV bits */ + val = wm8350_reg_read(wm8350, volt_reg) & ~WM8350_LDO1_VSEL_MASK; + wm8350_reg_write(wm8350, volt_reg, + val | wm8350_ldo_mvolts_to_val(mV)); + return 0; +} + +static int wm8350_ldo_set_suspend_enable(struct regulator_dev *rdev) +{ + struct wm8350 *wm8350 = rdev_get_drvdata(rdev); + int volt_reg, ldo = rdev_get_id(rdev); + u16 val; + + switch (ldo) { + case WM8350_LDO_1: + volt_reg = WM8350_LDO1_LOW_POWER; + break; + case WM8350_LDO_2: + volt_reg = WM8350_LDO2_LOW_POWER; + break; + case WM8350_LDO_3: + volt_reg = WM8350_LDO3_LOW_POWER; + break; + case WM8350_LDO_4: + volt_reg = WM8350_LDO4_LOW_POWER; + break; + default: + return -EINVAL; + } + + /* all LDOs have same mV bits */ + val = wm8350_reg_read(wm8350, volt_reg) & ~WM8350_LDO1_HIB_MODE_MASK; + wm8350_reg_write(wm8350, volt_reg, val); + return 0; +} + +static int wm8350_ldo_set_suspend_disable(struct regulator_dev *rdev) +{ + struct wm8350 *wm8350 = rdev_get_drvdata(rdev); + int volt_reg, ldo = rdev_get_id(rdev); + u16 val; + + switch (ldo) { + case WM8350_LDO_1: + volt_reg = WM8350_LDO1_LOW_POWER; + break; + case WM8350_LDO_2: + volt_reg = WM8350_LDO2_LOW_POWER; + break; + case WM8350_LDO_3: + volt_reg = WM8350_LDO3_LOW_POWER; + break; + case WM8350_LDO_4: + volt_reg = WM8350_LDO4_LOW_POWER; + break; + default: + return -EINVAL; + } + + /* all LDOs have same mV bits */ + val = wm8350_reg_read(wm8350, volt_reg) & ~WM8350_LDO1_HIB_MODE_MASK; + wm8350_reg_write(wm8350, volt_reg, WM8350_LDO1_HIB_MODE_DIS); + return 0; +} + +static int wm8350_ldo_set_voltage(struct regulator_dev *rdev, int min_uV, + int max_uV) +{ + struct wm8350 *wm8350 = rdev_get_drvdata(rdev); + int volt_reg, ldo = rdev_get_id(rdev), mV, min_mV = min_uV / 1000, + max_mV = max_uV / 1000; + u16 val; + + if (min_mV < 900 || min_mV > 3300) + return -EINVAL; + if (max_mV < 900 || max_mV > 3300) + return -EINVAL; + + if (min_mV < 1800) { + /* step size is 50mV < 1800mV */ + mV = (min_mV - 851) / 50; + if (wm8350_ldo_val_to_mvolts(mV) > max_mV) + return -EINVAL; + BUG_ON(wm8350_ldo_val_to_mvolts(mV) < min_mV); + } else { + /* step size is 100mV > 1800mV */ + mV = ((min_mV - 1701) / 100) + 16; + if (wm8350_ldo_val_to_mvolts(mV) > max_mV) + return -EINVAL; + BUG_ON(wm8350_ldo_val_to_mvolts(mV) < min_mV); + } + + switch (ldo) { + case WM8350_LDO_1: + volt_reg = WM8350_LDO1_CONTROL; + break; + case WM8350_LDO_2: + volt_reg = WM8350_LDO2_CONTROL; + break; + case WM8350_LDO_3: + volt_reg = WM8350_LDO3_CONTROL; + break; + case WM8350_LDO_4: + volt_reg = WM8350_LDO4_CONTROL; + break; + default: + return -EINVAL; + } + + /* all LDOs have same mV bits */ + val = wm8350_reg_read(wm8350, volt_reg) & ~WM8350_LDO1_VSEL_MASK; + wm8350_reg_write(wm8350, volt_reg, val | mV); + return 0; +} + +static int wm8350_ldo_get_voltage(struct regulator_dev *rdev) +{ + struct wm8350 *wm8350 = rdev_get_drvdata(rdev); + int volt_reg, ldo = rdev_get_id(rdev); + u16 val; + + switch (ldo) { + case WM8350_LDO_1: + volt_reg = WM8350_LDO1_CONTROL; + break; + case WM8350_LDO_2: + volt_reg = WM8350_LDO2_CONTROL; + break; + case WM8350_LDO_3: + volt_reg = WM8350_LDO3_CONTROL; + break; + case WM8350_LDO_4: + volt_reg = WM8350_LDO4_CONTROL; + break; + default: + return -EINVAL; + } + + /* all LDOs have same mV bits */ + val = wm8350_reg_read(wm8350, volt_reg) & WM8350_LDO1_VSEL_MASK; + return wm8350_ldo_val_to_mvolts(val) * 1000; +} + +int wm8350_dcdc_set_slot(struct wm8350 *wm8350, int dcdc, u16 start, + u16 stop, u16 fault) +{ + int slot_reg; + u16 val; + + dev_dbg(wm8350->dev, "%s %d start %d stop %d\n", + __func__, dcdc, start, stop); + + /* slot valid ? */ + if (start > 15 || stop > 15) + return -EINVAL; + + switch (dcdc) { + case WM8350_DCDC_1: + slot_reg = WM8350_DCDC1_TIMEOUTS; + break; + case WM8350_DCDC_2: + slot_reg = WM8350_DCDC2_TIMEOUTS; + break; + case WM8350_DCDC_3: + slot_reg = WM8350_DCDC3_TIMEOUTS; + break; + case WM8350_DCDC_4: + slot_reg = WM8350_DCDC4_TIMEOUTS; + break; + case WM8350_DCDC_5: + slot_reg = WM8350_DCDC5_TIMEOUTS; + break; + case WM8350_DCDC_6: + slot_reg = WM8350_DCDC6_TIMEOUTS; + break; + default: + return -EINVAL; + } + + val = wm8350_reg_read(wm8350, slot_reg) & + ~(WM8350_DC1_ENSLOT_MASK | WM8350_DC1_SDSLOT_MASK | + WM8350_DC1_ERRACT_MASK); + wm8350_reg_write(wm8350, slot_reg, + val | (start << WM8350_DC1_ENSLOT_SHIFT) | + (stop << WM8350_DC1_SDSLOT_SHIFT) | + (fault << WM8350_DC1_ERRACT_SHIFT)); + + return 0; +} +EXPORT_SYMBOL_GPL(wm8350_dcdc_set_slot); + +int wm8350_ldo_set_slot(struct wm8350 *wm8350, int ldo, u16 start, u16 stop) +{ + int slot_reg; + u16 val; + + dev_dbg(wm8350->dev, "%s %d start %d stop %d\n", + __func__, ldo, start, stop); + + /* slot valid ? */ + if (start > 15 || stop > 15) + return -EINVAL; + + switch (ldo) { + case WM8350_LDO_1: + slot_reg = WM8350_LDO1_TIMEOUTS; + break; + case WM8350_LDO_2: + slot_reg = WM8350_LDO2_TIMEOUTS; + break; + case WM8350_LDO_3: + slot_reg = WM8350_LDO3_TIMEOUTS; + break; + case WM8350_LDO_4: + slot_reg = WM8350_LDO4_TIMEOUTS; + break; + default: + return -EINVAL; + } + + val = wm8350_reg_read(wm8350, slot_reg) & ~WM8350_LDO1_SDSLOT_MASK; + wm8350_reg_write(wm8350, slot_reg, val | ((start << 10) | (stop << 6))); + return 0; +} +EXPORT_SYMBOL_GPL(wm8350_ldo_set_slot); + +int wm8350_dcdc25_set_mode(struct wm8350 *wm8350, int dcdc, u16 mode, + u16 ilim, u16 ramp, u16 feedback) +{ + u16 val; + + dev_dbg(wm8350->dev, "%s %d mode: %s %s\n", __func__, dcdc, + mode ? "normal" : "boost", ilim ? "low" : "normal"); + + switch (dcdc) { + case WM8350_DCDC_2: + val = wm8350_reg_read(wm8350, WM8350_DCDC2_CONTROL) + & ~(WM8350_DC2_MODE_MASK | WM8350_DC2_ILIM_MASK | + WM8350_DC2_RMP_MASK | WM8350_DC2_FBSRC_MASK); + wm8350_reg_write(wm8350, WM8350_DCDC2_CONTROL, val | + (mode << WM8350_DC2_MODE_SHIFT) | + (ilim << WM8350_DC2_ILIM_SHIFT) | + (ramp << WM8350_DC2_RMP_SHIFT) | + (feedback << WM8350_DC2_FBSRC_SHIFT)); + break; + case WM8350_DCDC_5: + val = wm8350_reg_read(wm8350, WM8350_DCDC5_CONTROL) + & ~(WM8350_DC5_MODE_MASK | WM8350_DC5_ILIM_MASK | + WM8350_DC5_RMP_MASK | WM8350_DC5_FBSRC_MASK); + wm8350_reg_write(wm8350, WM8350_DCDC5_CONTROL, val | + (mode << WM8350_DC5_MODE_SHIFT) | + (ilim << WM8350_DC5_ILIM_SHIFT) | + (ramp << WM8350_DC5_RMP_SHIFT) | + (feedback << WM8350_DC5_FBSRC_SHIFT)); + break; + default: + return -EINVAL; + } + + return 0; +} +EXPORT_SYMBOL_GPL(wm8350_dcdc25_set_mode); + +static int wm8350_dcdc_enable(struct regulator_dev *rdev) +{ + struct wm8350 *wm8350 = rdev_get_drvdata(rdev); + int dcdc = rdev_get_id(rdev); + u16 shift; + + if (dcdc < WM8350_DCDC_1 || dcdc > WM8350_DCDC_6) + return -EINVAL; + + shift = dcdc - WM8350_DCDC_1; + wm8350_set_bits(wm8350, WM8350_DCDC_LDO_REQUESTED, 1 << shift); + return 0; +} + +static int wm8350_dcdc_disable(struct regulator_dev *rdev) +{ + struct wm8350 *wm8350 = rdev_get_drvdata(rdev); + int dcdc = rdev_get_id(rdev); + u16 shift; + + if (dcdc < WM8350_DCDC_1 || dcdc > WM8350_DCDC_6) + return -EINVAL; + + shift = dcdc - WM8350_DCDC_1; + wm8350_clear_bits(wm8350, WM8350_DCDC_LDO_REQUESTED, 1 << shift); + + return 0; +} + +static int wm8350_ldo_enable(struct regulator_dev *rdev) +{ + struct wm8350 *wm8350 = rdev_get_drvdata(rdev); + int ldo = rdev_get_id(rdev); + u16 shift; + + if (ldo < WM8350_LDO_1 || ldo > WM8350_LDO_4) + return -EINVAL; + + shift = (ldo - WM8350_LDO_1) + 8; + wm8350_set_bits(wm8350, WM8350_DCDC_LDO_REQUESTED, 1 << shift); + return 0; +} + +static int wm8350_ldo_disable(struct regulator_dev *rdev) +{ + struct wm8350 *wm8350 = rdev_get_drvdata(rdev); + int ldo = rdev_get_id(rdev); + u16 shift; + + if (ldo < WM8350_LDO_1 || ldo > WM8350_LDO_4) + return -EINVAL; + + shift = (ldo - WM8350_LDO_1) + 8; + wm8350_clear_bits(wm8350, WM8350_DCDC_LDO_REQUESTED, 1 << shift); + return 0; +} + +static int force_continuous_enable(struct wm8350 *wm8350, int dcdc, int enable) +{ + int reg = 0, ret; + + switch (dcdc) { + case WM8350_DCDC_1: + reg = WM8350_DCDC1_FORCE_PWM; + break; + case WM8350_DCDC_3: + reg = WM8350_DCDC3_FORCE_PWM; + break; + case WM8350_DCDC_4: + reg = WM8350_DCDC4_FORCE_PWM; + break; + case WM8350_DCDC_6: + reg = WM8350_DCDC6_FORCE_PWM; + break; + default: + return -EINVAL; + } + + if (enable) + ret = wm8350_set_bits(wm8350, reg, + WM8350_DCDC1_FORCE_PWM_ENA); + else + ret = wm8350_clear_bits(wm8350, reg, + WM8350_DCDC1_FORCE_PWM_ENA); + return ret; +} + +static int wm8350_dcdc_set_mode(struct regulator_dev *rdev, unsigned int mode) +{ + struct wm8350 *wm8350 = rdev_get_drvdata(rdev); + int dcdc = rdev_get_id(rdev); + u16 val; + + if (dcdc < WM8350_DCDC_1 || dcdc > WM8350_DCDC_6) + return -EINVAL; + + if (dcdc == WM8350_DCDC_2 || dcdc == WM8350_DCDC_5) + return -EINVAL; + + val = 1 << (dcdc - WM8350_DCDC_1); + + switch (mode) { + case REGULATOR_MODE_FAST: + /* force continuous mode */ + wm8350_set_bits(wm8350, WM8350_DCDC_ACTIVE_OPTIONS, val); + wm8350_clear_bits(wm8350, WM8350_DCDC_SLEEP_OPTIONS, val); + force_continuous_enable(wm8350, dcdc, 1); + break; + case REGULATOR_MODE_NORMAL: + /* active / pulse skipping */ + wm8350_set_bits(wm8350, WM8350_DCDC_ACTIVE_OPTIONS, val); + wm8350_clear_bits(wm8350, WM8350_DCDC_SLEEP_OPTIONS, val); + force_continuous_enable(wm8350, dcdc, 0); + break; + case REGULATOR_MODE_IDLE: + /* standby mode */ + force_continuous_enable(wm8350, dcdc, 0); + wm8350_clear_bits(wm8350, WM8350_DCDC_SLEEP_OPTIONS, val); + wm8350_clear_bits(wm8350, WM8350_DCDC_ACTIVE_OPTIONS, val); + break; + case REGULATOR_MODE_STANDBY: + /* LDO mode */ + force_continuous_enable(wm8350, dcdc, 0); + wm8350_set_bits(wm8350, WM8350_DCDC_SLEEP_OPTIONS, val); + break; + } + + return 0; +} + +static unsigned int wm8350_dcdc_get_mode(struct regulator_dev *rdev) +{ + struct wm8350 *wm8350 = rdev_get_drvdata(rdev); + int dcdc = rdev_get_id(rdev); + u16 mask, sleep, active, force; + int mode = REGULATOR_MODE_NORMAL; + + if (dcdc < WM8350_DCDC_1 || dcdc > WM8350_DCDC_6) + return -EINVAL; + + if (dcdc == WM8350_DCDC_2 || dcdc == WM8350_DCDC_5) + return -EINVAL; + + mask = 1 << (dcdc - WM8350_DCDC_1); + active = wm8350_reg_read(wm8350, WM8350_DCDC_ACTIVE_OPTIONS) & mask; + sleep = wm8350_reg_read(wm8350, WM8350_DCDC_SLEEP_OPTIONS) & mask; + force = wm8350_reg_read(wm8350, WM8350_DCDC1_FORCE_PWM) + & WM8350_DCDC1_FORCE_PWM_ENA; + dev_dbg(wm8350->dev, "mask %x active %x sleep %x force %x", + mask, active, sleep, force); + + if (active && !sleep) { + if (force) + mode = REGULATOR_MODE_FAST; + else + mode = REGULATOR_MODE_NORMAL; + } else if (!active && !sleep) + mode = REGULATOR_MODE_IDLE; + else if (!sleep) + mode = REGULATOR_MODE_STANDBY; + + return mode; +} + +static unsigned int wm8350_ldo_get_mode(struct regulator_dev *rdev) +{ + return REGULATOR_MODE_NORMAL; +} + +struct wm8350_dcdc_efficiency { + int uA_load_min; + int uA_load_max; + unsigned int mode; +}; + +static const struct wm8350_dcdc_efficiency dcdc1_6_efficiency[] = { + {0, 10000, REGULATOR_MODE_STANDBY}, /* 0 - 10mA - LDO */ + {10000, 100000, REGULATOR_MODE_IDLE}, /* 10mA - 100mA - Standby */ + {100000, 1000000, REGULATOR_MODE_NORMAL}, /* > 100mA - Active */ + {-1, -1, REGULATOR_MODE_NORMAL}, +}; + +static const struct wm8350_dcdc_efficiency dcdc3_4_efficiency[] = { + {0, 10000, REGULATOR_MODE_STANDBY}, /* 0 - 10mA - LDO */ + {10000, 100000, REGULATOR_MODE_IDLE}, /* 10mA - 100mA - Standby */ + {100000, 800000, REGULATOR_MODE_NORMAL}, /* > 100mA - Active */ + {-1, -1, REGULATOR_MODE_NORMAL}, +}; + +static unsigned int get_mode(int uA, const struct wm8350_dcdc_efficiency *eff) +{ + int i = 0; + + while (eff[i].uA_load_min != -1) { + if (uA >= eff[i].uA_load_min && uA <= eff[i].uA_load_max) + return eff[i].mode; + } + return REGULATOR_MODE_NORMAL; +} + +/* Query the regulator for it's most efficient mode @ uV,uA + * WM8350 regulator efficiency is pretty similar over + * different input and output uV. + */ +static unsigned int wm8350_dcdc_get_optimum_mode(struct regulator_dev *rdev, + int input_uV, int output_uV, + int output_uA) +{ + int dcdc = rdev_get_id(rdev), mode; + + switch (dcdc) { + case WM8350_DCDC_1: + case WM8350_DCDC_6: + mode = get_mode(output_uA, dcdc1_6_efficiency); + break; + case WM8350_DCDC_3: + case WM8350_DCDC_4: + mode = get_mode(output_uA, dcdc3_4_efficiency); + break; + default: + mode = REGULATOR_MODE_NORMAL; + break; + } + return mode; +} + +static int wm8350_dcdc_is_enabled(struct regulator_dev *rdev) +{ + struct wm8350 *wm8350 = rdev_get_drvdata(rdev); + int dcdc = rdev_get_id(rdev), shift; + + if (dcdc < WM8350_DCDC_1 || dcdc > WM8350_DCDC_6) + return -EINVAL; + + shift = dcdc - WM8350_DCDC_1; + return wm8350_reg_read(wm8350, WM8350_DCDC_LDO_REQUESTED) + & (1 << shift); +} + +static int wm8350_ldo_is_enabled(struct regulator_dev *rdev) +{ + struct wm8350 *wm8350 = rdev_get_drvdata(rdev); + int ldo = rdev_get_id(rdev), shift; + + if (ldo < WM8350_LDO_1 || ldo > WM8350_LDO_4) + return -EINVAL; + + shift = (ldo - WM8350_LDO_1) + 8; + return wm8350_reg_read(wm8350, WM8350_DCDC_LDO_REQUESTED) + & (1 << shift); +} + +static struct regulator_ops wm8350_dcdc_ops = { + .set_voltage = wm8350_dcdc_set_voltage, + .get_voltage = wm8350_dcdc_get_voltage, + .enable = wm8350_dcdc_enable, + .disable = wm8350_dcdc_disable, + .get_mode = wm8350_dcdc_get_mode, + .set_mode = wm8350_dcdc_set_mode, + .get_optimum_mode = wm8350_dcdc_get_optimum_mode, + .is_enabled = wm8350_dcdc_is_enabled, + .set_suspend_voltage = wm8350_dcdc_set_suspend_voltage, + .set_suspend_enable = wm8350_dcdc_set_suspend_enable, + .set_suspend_disable = wm8350_dcdc_set_suspend_disable, + .set_suspend_mode = wm8350_dcdc_set_suspend_mode, +}; + +static struct regulator_ops wm8350_dcdc2_5_ops = { + .enable = wm8350_dcdc_enable, + .disable = wm8350_dcdc_disable, + .is_enabled = wm8350_dcdc_is_enabled, + .set_suspend_enable = wm8350_dcdc25_set_suspend_enable, + .set_suspend_disable = wm8350_dcdc25_set_suspend_disable, +}; + +static struct regulator_ops wm8350_ldo_ops = { + .set_voltage = wm8350_ldo_set_voltage, + .get_voltage = wm8350_ldo_get_voltage, + .enable = wm8350_ldo_enable, + .disable = wm8350_ldo_disable, + .is_enabled = wm8350_ldo_is_enabled, + .get_mode = wm8350_ldo_get_mode, + .set_suspend_voltage = wm8350_ldo_set_suspend_voltage, + .set_suspend_enable = wm8350_ldo_set_suspend_enable, + .set_suspend_disable = wm8350_ldo_set_suspend_disable, +}; + +static struct regulator_ops wm8350_isink_ops = { + .set_current_limit = wm8350_isink_set_current, + .get_current_limit = wm8350_isink_get_current, + .enable = wm8350_isink_enable, + .disable = wm8350_isink_disable, + .is_enabled = wm8350_isink_is_enabled, +}; + +static struct regulator_desc wm8350_reg[NUM_WM8350_REGULATORS] = { + { + .name = "DCDC1", + .id = WM8350_DCDC_1, + .ops = &wm8350_dcdc_ops, + .irq = WM8350_IRQ_UV_DC1, + .type = REGULATOR_VOLTAGE, + .owner = THIS_MODULE, + }, + { + .name = "DCDC2", + .id = WM8350_DCDC_2, + .ops = &wm8350_dcdc2_5_ops, + .irq = WM8350_IRQ_UV_DC2, + .type = REGULATOR_VOLTAGE, + .owner = THIS_MODULE, + }, + { + .name = "DCDC3", + .id = WM8350_DCDC_3, + .ops = &wm8350_dcdc_ops, + .irq = WM8350_IRQ_UV_DC3, + .type = REGULATOR_VOLTAGE, + .owner = THIS_MODULE, + }, + { + .name = "DCDC4", + .id = WM8350_DCDC_4, + .ops = &wm8350_dcdc_ops, + .irq = WM8350_IRQ_UV_DC4, + .type = REGULATOR_VOLTAGE, + .owner = THIS_MODULE, + }, + { + .name = "DCDC5", + .id = WM8350_DCDC_5, + .ops = &wm8350_dcdc2_5_ops, + .irq = WM8350_IRQ_UV_DC5, + .type = REGULATOR_VOLTAGE, + .owner = THIS_MODULE, + }, + { + .name = "DCDC6", + .id = WM8350_DCDC_6, + .ops = &wm8350_dcdc_ops, + .irq = WM8350_IRQ_UV_DC6, + .type = REGULATOR_VOLTAGE, + .owner = THIS_MODULE, + }, + { + .name = "LDO1", + .id = WM8350_LDO_1, + .ops = &wm8350_ldo_ops, + .irq = WM8350_IRQ_UV_LDO1, + .type = REGULATOR_VOLTAGE, + .owner = THIS_MODULE, + }, + { + .name = "LDO2", + .id = WM8350_LDO_2, + .ops = &wm8350_ldo_ops, + .irq = WM8350_IRQ_UV_LDO2, + .type = REGULATOR_VOLTAGE, + .owner = THIS_MODULE, + }, + { + .name = "LDO3", + .id = WM8350_LDO_3, + .ops = &wm8350_ldo_ops, + .irq = WM8350_IRQ_UV_LDO3, + .type = REGULATOR_VOLTAGE, + .owner = THIS_MODULE, + }, + { + .name = "LDO4", + .id = WM8350_LDO_4, + .ops = &wm8350_ldo_ops, + .irq = WM8350_IRQ_UV_LDO4, + .type = REGULATOR_VOLTAGE, + .owner = THIS_MODULE, + }, + { + .name = "ISINKA", + .id = WM8350_ISINK_A, + .ops = &wm8350_isink_ops, + .irq = WM8350_IRQ_CS1, + .type = REGULATOR_CURRENT, + .owner = THIS_MODULE, + }, + { + .name = "ISINKB", + .id = WM8350_ISINK_B, + .ops = &wm8350_isink_ops, + .irq = WM8350_IRQ_CS2, + .type = REGULATOR_CURRENT, + .owner = THIS_MODULE, + }, +}; + +static void pmic_uv_handler(struct wm8350 *wm8350, int irq, void *data) +{ + struct regulator_dev *rdev = (struct regulator_dev *)data; + + if (irq == WM8350_IRQ_CS1 || irq == WM8350_IRQ_CS2) + regulator_notifier_call_chain(rdev, + REGULATOR_EVENT_REGULATION_OUT, + wm8350); + else + regulator_notifier_call_chain(rdev, + REGULATOR_EVENT_UNDER_VOLTAGE, + wm8350); +} + +static int wm8350_regulator_probe(struct platform_device *pdev) +{ + struct wm8350 *wm8350 = dev_get_drvdata(&pdev->dev); + struct regulator_dev *rdev; + int ret; + u16 val; + + if (pdev->id < WM8350_DCDC_1 || pdev->id > WM8350_ISINK_B) + return -ENODEV; + + /* do any regulatior specific init */ + switch (pdev->id) { + case WM8350_DCDC_1: + val = wm8350_reg_read(wm8350, WM8350_DCDC1_LOW_POWER); + wm8350->pmic.dcdc1_hib_mode = val & WM8350_DCDC_HIB_MODE_MASK; + break; + case WM8350_DCDC_3: + val = wm8350_reg_read(wm8350, WM8350_DCDC3_LOW_POWER); + wm8350->pmic.dcdc3_hib_mode = val & WM8350_DCDC_HIB_MODE_MASK; + break; + case WM8350_DCDC_4: + val = wm8350_reg_read(wm8350, WM8350_DCDC4_LOW_POWER); + wm8350->pmic.dcdc4_hib_mode = val & WM8350_DCDC_HIB_MODE_MASK; + break; + case WM8350_DCDC_6: + val = wm8350_reg_read(wm8350, WM8350_DCDC6_LOW_POWER); + wm8350->pmic.dcdc6_hib_mode = val & WM8350_DCDC_HIB_MODE_MASK; + break; + } + + + /* register regulator */ + rdev = regulator_register(&wm8350_reg[pdev->id], &pdev->dev, + dev_get_drvdata(&pdev->dev)); + if (IS_ERR(rdev)) { + dev_err(&pdev->dev, "failed to register %s\n", + wm8350_reg[pdev->id].name); + return PTR_ERR(rdev); + } + + /* register regulator IRQ */ + ret = wm8350_register_irq(wm8350, wm8350_reg[pdev->id].irq, + pmic_uv_handler, rdev); + if (ret < 0) { + regulator_unregister(rdev); + dev_err(&pdev->dev, "failed to register regulator %s IRQ\n", + wm8350_reg[pdev->id].name); + return ret; + } + + wm8350_unmask_irq(wm8350, wm8350_reg[pdev->id].irq); + + return 0; +} + +static int wm8350_regulator_remove(struct platform_device *pdev) +{ + struct regulator_dev *rdev = platform_get_drvdata(pdev); + struct wm8350 *wm8350 = rdev_get_drvdata(rdev); + + wm8350_mask_irq(wm8350, wm8350_reg[pdev->id].irq); + wm8350_free_irq(wm8350, wm8350_reg[pdev->id].irq); + + regulator_unregister(rdev); + + return 0; +} + +int wm8350_register_regulator(struct wm8350 *wm8350, int reg, + struct regulator_init_data *initdata) +{ + struct platform_device *pdev; + int ret; + + if (wm8350->pmic.pdev[reg]) + return -EBUSY; + + pdev = platform_device_alloc("wm8350-regulator", reg); + if (!pdev) + return -ENOMEM; + + wm8350->pmic.pdev[reg] = pdev; + + initdata->driver_data = wm8350; + + pdev->dev.platform_data = initdata; + pdev->dev.parent = wm8350->dev; + platform_set_drvdata(pdev, wm8350); + + ret = platform_device_add(pdev); + + if (ret != 0) { + dev_err(wm8350->dev, "Failed to register regulator %d: %d\n", + reg, ret); + platform_device_del(pdev); + wm8350->pmic.pdev[reg] = NULL; + } + + return ret; +} +EXPORT_SYMBOL_GPL(wm8350_register_regulator); + +static struct platform_driver wm8350_regulator_driver = { + .probe = wm8350_regulator_probe, + .remove = wm8350_regulator_remove, + .driver = { + .name = "wm8350-regulator", + }, +}; + +static int __init wm8350_regulator_init(void) +{ + return platform_driver_register(&wm8350_regulator_driver); +} +subsys_initcall(wm8350_regulator_init); + +static void __exit wm8350_regulator_exit(void) +{ + platform_driver_unregister(&wm8350_regulator_driver); +} +module_exit(wm8350_regulator_exit); + +/* Module information */ +MODULE_AUTHOR("Liam Girdwood"); +MODULE_DESCRIPTION("WM8350 voltage and current regulator driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/regulator/wm8400-regulator.c b/drivers/regulator/wm8400-regulator.c new file mode 100644 index 000000000000..48b372e038a8 --- /dev/null +++ b/drivers/regulator/wm8400-regulator.c @@ -0,0 +1,368 @@ +/* + * Regulator support for WM8400 + * + * Copyright 2008 Wolfson Microelectronics PLC. + * + * Author: Mark Brown <broonie@opensource.wolfsonmicro.com> + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + */ + +#include <linux/bug.h> +#include <linux/err.h> +#include <linux/kernel.h> +#include <linux/regulator/driver.h> +#include <linux/mfd/wm8400-private.h> + +static int wm8400_ldo_is_enabled(struct regulator_dev *dev) +{ + struct wm8400 *wm8400 = rdev_get_drvdata(dev); + u16 val; + + val = wm8400_reg_read(wm8400, WM8400_LDO1_CONTROL + rdev_get_id(dev)); + return (val & WM8400_LDO1_ENA) != 0; +} + +static int wm8400_ldo_enable(struct regulator_dev *dev) +{ + struct wm8400 *wm8400 = rdev_get_drvdata(dev); + + return wm8400_set_bits(wm8400, WM8400_LDO1_CONTROL + rdev_get_id(dev), + WM8400_LDO1_ENA, WM8400_LDO1_ENA); +} + +static int wm8400_ldo_disable(struct regulator_dev *dev) +{ + struct wm8400 *wm8400 = rdev_get_drvdata(dev); + + return wm8400_set_bits(wm8400, WM8400_LDO1_CONTROL + rdev_get_id(dev), + WM8400_LDO1_ENA, 0); +} + +static int wm8400_ldo_get_voltage(struct regulator_dev *dev) +{ + struct wm8400 *wm8400 = rdev_get_drvdata(dev); + u16 val; + + val = wm8400_reg_read(wm8400, WM8400_LDO1_CONTROL + rdev_get_id(dev)); + val &= WM8400_LDO1_VSEL_MASK; + + if (val < 15) + return 900000 + (val * 50000); + else + return 1600000 + ((val - 14) * 100000); +} + +static int wm8400_ldo_set_voltage(struct regulator_dev *dev, + int min_uV, int max_uV) +{ + struct wm8400 *wm8400 = rdev_get_drvdata(dev); + u16 val; + + if (min_uV < 900000 || min_uV > 3300000) + return -EINVAL; + + if (min_uV < 1700000) { + /* Steps of 50mV from 900mV; */ + val = (min_uV - 850001) / 50000; + + if ((val * 50000) + 900000 > max_uV) + return -EINVAL; + BUG_ON((val * 50000) + 900000 < min_uV); + } else { + /* Steps of 100mV from 1700mV */ + val = ((min_uV - 1600001) / 100000); + + if ((val * 100000) + 1700000 > max_uV) + return -EINVAL; + BUG_ON((val * 100000) + 1700000 < min_uV); + + val += 0xf; + } + + return wm8400_set_bits(wm8400, WM8400_LDO1_CONTROL + rdev_get_id(dev), + WM8400_LDO1_VSEL_MASK, val); +} + +static struct regulator_ops wm8400_ldo_ops = { + .is_enabled = wm8400_ldo_is_enabled, + .enable = wm8400_ldo_enable, + .disable = wm8400_ldo_disable, + .get_voltage = wm8400_ldo_get_voltage, + .set_voltage = wm8400_ldo_set_voltage, +}; + +static int wm8400_dcdc_is_enabled(struct regulator_dev *dev) +{ + struct wm8400 *wm8400 = rdev_get_drvdata(dev); + int offset = (rdev_get_id(dev) - WM8400_DCDC1) * 2; + u16 val; + + val = wm8400_reg_read(wm8400, WM8400_DCDC1_CONTROL_1 + offset); + return (val & WM8400_DC1_ENA) != 0; +} + +static int wm8400_dcdc_enable(struct regulator_dev *dev) +{ + struct wm8400 *wm8400 = rdev_get_drvdata(dev); + int offset = (rdev_get_id(dev) - WM8400_DCDC1) * 2; + + return wm8400_set_bits(wm8400, WM8400_DCDC1_CONTROL_1 + offset, + WM8400_DC1_ENA, WM8400_DC1_ENA); +} + +static int wm8400_dcdc_disable(struct regulator_dev *dev) +{ + struct wm8400 *wm8400 = rdev_get_drvdata(dev); + int offset = (rdev_get_id(dev) - WM8400_DCDC1) * 2; + + return wm8400_set_bits(wm8400, WM8400_DCDC1_CONTROL_1 + offset, + WM8400_DC1_ENA, 0); +} + +static int wm8400_dcdc_get_voltage(struct regulator_dev *dev) +{ + struct wm8400 *wm8400 = rdev_get_drvdata(dev); + u16 val; + int offset = (rdev_get_id(dev) - WM8400_DCDC1) * 2; + + val = wm8400_reg_read(wm8400, WM8400_DCDC1_CONTROL_1 + offset); + val &= WM8400_DC1_VSEL_MASK; + + return 850000 + (25000 * val); +} + +static int wm8400_dcdc_set_voltage(struct regulator_dev *dev, + int min_uV, int max_uV) +{ + struct wm8400 *wm8400 = rdev_get_drvdata(dev); + u16 val; + int offset = (rdev_get_id(dev) - WM8400_DCDC1) * 2; + + if (min_uV < 850000) + return -EINVAL; + + val = (min_uV - 825001) / 25000; + + if (850000 + (25000 * val) > max_uV) + return -EINVAL; + BUG_ON(850000 + (25000 * val) < min_uV); + + return wm8400_set_bits(wm8400, WM8400_DCDC1_CONTROL_1 + offset, + WM8400_DC1_VSEL_MASK, val); +} + +static unsigned int wm8400_dcdc_get_mode(struct regulator_dev *dev) +{ + struct wm8400 *wm8400 = rdev_get_drvdata(dev); + int offset = (rdev_get_id(dev) - WM8400_DCDC1) * 2; + u16 data[2]; + int ret; + + ret = wm8400_block_read(wm8400, WM8400_DCDC1_CONTROL_1 + offset, 2, + data); + if (ret != 0) + return 0; + + /* Datasheet: hibernate */ + if (data[0] & WM8400_DC1_SLEEP) + return REGULATOR_MODE_STANDBY; + + /* Datasheet: standby */ + if (!(data[0] & WM8400_DC1_ACTIVE)) + return REGULATOR_MODE_IDLE; + + /* Datasheet: active with or without force PWM */ + if (data[1] & WM8400_DC1_FRC_PWM) + return REGULATOR_MODE_FAST; + else + return REGULATOR_MODE_NORMAL; +} + +static int wm8400_dcdc_set_mode(struct regulator_dev *dev, unsigned int mode) +{ + struct wm8400 *wm8400 = rdev_get_drvdata(dev); + int offset = (rdev_get_id(dev) - WM8400_DCDC1) * 2; + int ret; + + switch (mode) { + case REGULATOR_MODE_FAST: + /* Datasheet: active with force PWM */ + ret = wm8400_set_bits(wm8400, WM8400_DCDC1_CONTROL_2 + offset, + WM8400_DC1_FRC_PWM, WM8400_DC1_FRC_PWM); + if (ret != 0) + return ret; + + return wm8400_set_bits(wm8400, WM8400_DCDC1_CONTROL_1 + offset, + WM8400_DC1_ACTIVE | WM8400_DC1_SLEEP, + WM8400_DC1_ACTIVE); + + case REGULATOR_MODE_NORMAL: + /* Datasheet: active */ + ret = wm8400_set_bits(wm8400, WM8400_DCDC1_CONTROL_2 + offset, + WM8400_DC1_FRC_PWM, 0); + if (ret != 0) + return ret; + + return wm8400_set_bits(wm8400, WM8400_DCDC1_CONTROL_1 + offset, + WM8400_DC1_ACTIVE | WM8400_DC1_SLEEP, + WM8400_DC1_ACTIVE); + + 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); + + default: + return -EINVAL; + } +} + +static unsigned int wm8400_dcdc_get_optimum_mode(struct regulator_dev *dev, + int input_uV, int output_uV, + int load_uA) +{ + return REGULATOR_MODE_NORMAL; +} + +static struct regulator_ops wm8400_dcdc_ops = { + .is_enabled = wm8400_dcdc_is_enabled, + .enable = wm8400_dcdc_enable, + .disable = wm8400_dcdc_disable, + .get_voltage = wm8400_dcdc_get_voltage, + .set_voltage = wm8400_dcdc_set_voltage, + .get_mode = wm8400_dcdc_get_mode, + .set_mode = wm8400_dcdc_set_mode, + .get_optimum_mode = wm8400_dcdc_get_optimum_mode, +}; + +static struct regulator_desc regulators[] = { + { + .name = "LDO1", + .id = WM8400_LDO1, + .ops = &wm8400_ldo_ops, + .type = REGULATOR_VOLTAGE, + .owner = THIS_MODULE, + }, + { + .name = "LDO2", + .id = WM8400_LDO2, + .ops = &wm8400_ldo_ops, + .type = REGULATOR_VOLTAGE, + .owner = THIS_MODULE, + }, + { + .name = "LDO3", + .id = WM8400_LDO3, + .ops = &wm8400_ldo_ops, + .type = REGULATOR_VOLTAGE, + .owner = THIS_MODULE, + }, + { + .name = "LDO4", + .id = WM8400_LDO4, + .ops = &wm8400_ldo_ops, + .type = REGULATOR_VOLTAGE, + .owner = THIS_MODULE, + }, + { + .name = "DCDC1", + .id = WM8400_DCDC1, + .ops = &wm8400_dcdc_ops, + .type = REGULATOR_VOLTAGE, + .owner = THIS_MODULE, + }, + { + .name = "DCDC2", + .id = WM8400_DCDC2, + .ops = &wm8400_dcdc_ops, + .type = REGULATOR_VOLTAGE, + .owner = THIS_MODULE, + }, +}; + +static int __init wm8400_regulator_probe(struct platform_device *pdev) +{ + struct regulator_dev *rdev; + + rdev = regulator_register(®ulators[pdev->id], &pdev->dev, + pdev->dev.driver_data); + + if (IS_ERR(rdev)) + return PTR_ERR(rdev); + + return 0; +} + +static int __devexit wm8400_regulator_remove(struct platform_device *pdev) +{ + struct regulator_dev *rdev = platform_get_drvdata(pdev); + + regulator_unregister(rdev); + + return 0; +} + +static struct platform_driver wm8400_regulator_driver = { + .driver = { + .name = "wm8400-regulator", + }, + .probe = wm8400_regulator_probe, + .remove = __devexit_p(wm8400_regulator_remove), +}; + +/** + * wm8400_register_regulator - enable software control of a WM8400 regulator + * + * This function enables software control of a WM8400 regulator via + * the regulator API. It is intended to be called from the + * platform_init() callback of the WM8400 MFD driver. + * + * @param dev The WM8400 device to operate on. + * @param reg The regulator to control. + * @param initdata Regulator initdata for the regulator. + */ +int wm8400_register_regulator(struct device *dev, int reg, + struct regulator_init_data *initdata) +{ + struct wm8400 *wm8400 = dev->driver_data; + + if (wm8400->regulators[reg].name) + return -EBUSY; + + initdata->driver_data = wm8400; + + wm8400->regulators[reg].name = "wm8400-regulator"; + wm8400->regulators[reg].id = reg; + wm8400->regulators[reg].dev.parent = dev; + wm8400->regulators[reg].dev.driver_data = wm8400; + wm8400->regulators[reg].dev.platform_data = initdata; + + return platform_device_register(&wm8400->regulators[reg]); +} +EXPORT_SYMBOL_GPL(wm8400_register_regulator); + +static int __init wm8400_regulator_init(void) +{ + return platform_driver_register(&wm8400_regulator_driver); +} +module_init(wm8400_regulator_init); + +static void __exit wm8400_regulator_exit(void) +{ + platform_driver_unregister(&wm8400_regulator_driver); +} +module_exit(wm8400_regulator_exit); + +MODULE_AUTHOR("Mark Brown <broonie@opensource.wolfsonmicro.com>"); +MODULE_DESCRIPTION("WM8400 regulator driver"); +MODULE_LICENSE("GPL"); +MODULE_ALIAS("platform:wm8400-regulator"); diff --git a/drivers/rtc/rtc-cmos.c b/drivers/rtc/rtc-cmos.c index b23af0c2a869..963ad0b6a4e9 100644 --- a/drivers/rtc/rtc-cmos.c +++ b/drivers/rtc/rtc-cmos.c @@ -913,6 +913,92 @@ static inline int cmos_poweroff(struct device *dev) * predate even PNPBIOS should set up platform_bus devices. */ +#ifdef CONFIG_ACPI + +#include <linux/acpi.h> + +#ifdef CONFIG_PM +static u32 rtc_handler(void *context) +{ + acpi_clear_event(ACPI_EVENT_RTC); + acpi_disable_event(ACPI_EVENT_RTC, 0); + return ACPI_INTERRUPT_HANDLED; +} + +static inline void rtc_wake_setup(void) +{ + acpi_install_fixed_event_handler(ACPI_EVENT_RTC, rtc_handler, NULL); + /* + * After the RTC handler is installed, the Fixed_RTC event should + * be disabled. Only when the RTC alarm is set will it be enabled. + */ + acpi_clear_event(ACPI_EVENT_RTC); + acpi_disable_event(ACPI_EVENT_RTC, 0); +} + +static void rtc_wake_on(struct device *dev) +{ + acpi_clear_event(ACPI_EVENT_RTC); + acpi_enable_event(ACPI_EVENT_RTC, 0); +} + +static void rtc_wake_off(struct device *dev) +{ + acpi_disable_event(ACPI_EVENT_RTC, 0); +} +#else +#define rtc_wake_setup() do{}while(0) +#define rtc_wake_on NULL +#define rtc_wake_off NULL +#endif + +/* Every ACPI platform has a mc146818 compatible "cmos rtc". Here we find + * its device node and pass extra config data. This helps its driver use + * capabilities that the now-obsolete mc146818 didn't have, and informs it + * that this board's RTC is wakeup-capable (per ACPI spec). + */ +static struct cmos_rtc_board_info acpi_rtc_info; + +static void __devinit +cmos_wake_setup(struct device *dev) +{ + if (acpi_disabled) + return; + + rtc_wake_setup(); + acpi_rtc_info.wake_on = rtc_wake_on; + acpi_rtc_info.wake_off = rtc_wake_off; + + /* workaround bug in some ACPI tables */ + if (acpi_gbl_FADT.month_alarm && !acpi_gbl_FADT.day_alarm) { + dev_dbg(dev, "bogus FADT month_alarm (%d)\n", + acpi_gbl_FADT.month_alarm); + acpi_gbl_FADT.month_alarm = 0; + } + + acpi_rtc_info.rtc_day_alarm = acpi_gbl_FADT.day_alarm; + acpi_rtc_info.rtc_mon_alarm = acpi_gbl_FADT.month_alarm; + acpi_rtc_info.rtc_century = acpi_gbl_FADT.century; + + /* NOTE: S4_RTC_WAKE is NOT currently useful to Linux */ + if (acpi_gbl_FADT.flags & ACPI_FADT_S4_RTC_WAKE) + dev_info(dev, "RTC can wake from S4\n"); + + dev->platform_data = &acpi_rtc_info; + + /* RTC always wakes from S1/S2/S3, and often S4/STD */ + device_init_wakeup(dev, 1); +} + +#else + +static void __devinit +cmos_wake_setup(struct device *dev) +{ +} + +#endif + #ifdef CONFIG_PNP #include <linux/pnp.h> @@ -920,6 +1006,8 @@ static inline int cmos_poweroff(struct device *dev) static int __devinit cmos_pnp_probe(struct pnp_dev *pnp, const struct pnp_device_id *id) { + cmos_wake_setup(&pnp->dev); + if (pnp_port_start(pnp,0) == 0x70 && !pnp_irq_valid(pnp,0)) /* Some machines contain a PNP entry for the RTC, but * don't define the IRQ. It should always be safe to @@ -997,6 +1085,7 @@ static struct pnp_driver cmos_pnp_driver = { static int __init cmos_platform_probe(struct platform_device *pdev) { + cmos_wake_setup(&pdev->dev); return cmos_do_probe(&pdev->dev, platform_get_resource(pdev, IORESOURCE_IO, 0), platform_get_irq(pdev, 0)); @@ -1031,29 +1120,32 @@ static struct platform_driver cmos_platform_driver = { static int __init cmos_init(void) { + int retval = 0; + #ifdef CONFIG_PNP - if (pnp_platform_devices) - return pnp_register_driver(&cmos_pnp_driver); - else - return platform_driver_probe(&cmos_platform_driver, - cmos_platform_probe); -#else - return platform_driver_probe(&cmos_platform_driver, - cmos_platform_probe); -#endif /* CONFIG_PNP */ + pnp_register_driver(&cmos_pnp_driver); +#endif + + if (!cmos_rtc.dev) + retval = platform_driver_probe(&cmos_platform_driver, + cmos_platform_probe); + + if (retval == 0) + return 0; + +#ifdef CONFIG_PNP + pnp_unregister_driver(&cmos_pnp_driver); +#endif + return retval; } module_init(cmos_init); static void __exit cmos_exit(void) { #ifdef CONFIG_PNP - if (pnp_platform_devices) - pnp_unregister_driver(&cmos_pnp_driver); - else - platform_driver_unregister(&cmos_platform_driver); -#else + pnp_unregister_driver(&cmos_pnp_driver); +#endif platform_driver_unregister(&cmos_platform_driver); -#endif /* CONFIG_PNP */ } module_exit(cmos_exit); diff --git a/drivers/s390/net/claw.c b/drivers/s390/net/claw.c index 8f83fc994f50..f5e618562c5f 100644 --- a/drivers/s390/net/claw.c +++ b/drivers/s390/net/claw.c @@ -2913,7 +2913,7 @@ claw_new_device(struct ccwgroup_device *cgdev) if (ret != 0) { printk(KERN_WARNING "claw: ccw_device_set_online %s WRITE failed " - "with ret = %d\n", dev_name(&cgdev->cdev[WRITE]->dev) + "with ret = %d\n", dev_name(&cgdev->cdev[WRITE]->dev), ret); goto out; } diff --git a/drivers/scsi/Kconfig b/drivers/scsi/Kconfig index d3b211af4e1c..403ecad48d4b 100644 --- a/drivers/scsi/Kconfig +++ b/drivers/scsi/Kconfig @@ -1640,6 +1640,7 @@ config ATARI_SCSI tristate "Atari native SCSI support" depends on ATARI && SCSI select SCSI_SPI_ATTRS + select NVRAM ---help--- If you have an Atari with built-in NCR5380 SCSI controller (TT, Falcon, ...) say Y to get it supported. Of course also, if you have @@ -1670,14 +1671,6 @@ config ATARI_SCSI_RESET_BOOT boot process fractionally longer but may assist recovery from errors that leave the devices with SCSI operations partway completed. -config TT_DMA_EMUL - bool "Hades SCSI DMA emulator" - depends on ATARI_SCSI && HADES - help - This option enables code which emulates the TT SCSI DMA chip on the - Hades. This increases the SCSI transfer rates at least ten times - compared to PIO transfers. - config MAC_SCSI bool "Macintosh NCR5380 SCSI" depends on MAC && SCSI=y diff --git a/drivers/scsi/atari_dma_emul.c b/drivers/scsi/atari_dma_emul.c deleted file mode 100644 index cdc710ea00fa..000000000000 --- a/drivers/scsi/atari_dma_emul.c +++ /dev/null @@ -1,468 +0,0 @@ -/* - * atari_dma_emul.c -- TT SCSI DMA emulator for the Hades. - * - * Copyright 1997 Wout Klaren <W.Klaren@inter.nl.net> - * - * This file is subject to the terms and conditions of the GNU General Public - * License. See the file COPYING in the main directory of this archive - * for more details. - * - * This code was written using the Hades TOS source code as a - * reference. This source code can be found on the home page - * of Medusa Computer Systems. - * - * Version 0.1, 1997-09-24. - * - * This code should be considered experimental. It has only been - * tested on a Hades with a 68060. It might not work on a Hades - * with a 68040. Make backups of your hard drives before using - * this code. - */ - -#include <linux/compiler.h> -#include <asm/thread_info.h> -#include <asm/uaccess.h> - -#define hades_dma_ctrl (*(unsigned char *) 0xffff8717) -#define hades_psdm_reg (*(unsigned char *) 0xffff8741) - -#define TRANSFER_SIZE 16 - -struct m68040_frame { - unsigned long effaddr; /* effective address */ - unsigned short ssw; /* special status word */ - unsigned short wb3s; /* write back 3 status */ - unsigned short wb2s; /* write back 2 status */ - unsigned short wb1s; /* write back 1 status */ - unsigned long faddr; /* fault address */ - unsigned long wb3a; /* write back 3 address */ - unsigned long wb3d; /* write back 3 data */ - unsigned long wb2a; /* write back 2 address */ - unsigned long wb2d; /* write back 2 data */ - unsigned long wb1a; /* write back 1 address */ - unsigned long wb1dpd0; /* write back 1 data/push data 0*/ - unsigned long pd1; /* push data 1*/ - unsigned long pd2; /* push data 2*/ - unsigned long pd3; /* push data 3*/ -}; - -static void writeback (unsigned short wbs, unsigned long wba, - unsigned long wbd, void *old_buserr) -{ - mm_segment_t fs = get_fs(); - static void *save_buserr; - - __asm__ __volatile__ ("movec.l %%vbr,%%a0\n\t" - "move.l %0,8(%%a0)\n\t" - : - : "r" (&&bus_error) - : "a0" ); - - save_buserr = old_buserr; - - set_fs (MAKE_MM_SEG(wbs & WBTM_040)); - - switch (wbs & WBSIZ_040) { - case BA_SIZE_BYTE: - put_user (wbd & 0xff, (char *)wba); - break; - case BA_SIZE_WORD: - put_user (wbd & 0xffff, (short *)wba); - break; - case BA_SIZE_LONG: - put_user (wbd, (int *)wba); - break; - } - - set_fs (fs); - return; - -bus_error: - __asm__ __volatile__ ("cmp.l %0,2(%%sp)\n\t" - "bcs.s .jump_old\n\t" - "cmp.l %1,2(%%sp)\n\t" - "bls.s .restore_old\n" - ".jump_old:\n\t" - "move.l %2,-(%%sp)\n\t" - "rts\n" - ".restore_old:\n\t" - "move.l %%a0,-(%%sp)\n\t" - "movec.l %%vbr,%%a0\n\t" - "move.l %2,8(%%a0)\n\t" - "move.l (%%sp)+,%%a0\n\t" - "rte\n\t" - : - : "i" (writeback), "i" (&&bus_error), - "m" (save_buserr) ); -} - -/* - * static inline void set_restdata_reg(unsigned char *cur_addr) - * - * Set the rest data register if necessary. - */ - -static inline void set_restdata_reg(unsigned char *cur_addr) -{ - if (((long) cur_addr & ~3) != 0) - tt_scsi_dma.dma_restdata = - *((unsigned long *) ((long) cur_addr & ~3)); -} - -/* - * void hades_dma_emulator(int irq, void *dummy) - * - * This code emulates TT SCSI DMA on the Hades. - * - * Note the following: - * - * 1. When there is no byte available to read from the SCSI bus, or - * when a byte cannot yet bet written to the SCSI bus, a bus - * error occurs when reading or writing the pseudo DMA data - * register (hades_psdm_reg). We have to catch this bus error - * and try again to read or write the byte. If after several tries - * we still get a bus error, the interrupt handler is left. When - * the byte can be read or written, the interrupt handler is - * called again. - * - * 2. The SCSI interrupt must be disabled in this interrupt handler. - * - * 3. If we set the EOP signal, the SCSI controller still expects one - * byte to be read or written. Therefore the last byte is transferred - * separately, after setting the EOP signal. - * - * 4. When this function is left, the address pointer (start_addr) is - * converted to a physical address. Because it points one byte - * further than the last transferred byte, it can point outside the - * current page. If virt_to_phys() is called with this address we - * might get an access error. Therefore virt_to_phys() is called with - * start_addr - 1 if the count has reached zero. The result is - * increased with one. - */ - -static irqreturn_t hades_dma_emulator(int irq, void *dummy) -{ - unsigned long dma_base; - register unsigned long dma_cnt asm ("d3"); - static long save_buserr; - register unsigned long save_sp asm ("d4"); - register int tries asm ("d5"); - register unsigned char *start_addr asm ("a3"), *end_addr asm ("a4"); - register unsigned char *eff_addr; - register unsigned char *psdm_reg; - unsigned long rem; - - atari_disable_irq(IRQ_TT_MFP_SCSI); - - /* - * Read the dma address and count registers. - */ - - dma_base = SCSI_DMA_READ_P(dma_addr); - dma_cnt = SCSI_DMA_READ_P(dma_cnt); - - /* - * Check if DMA is still enabled. - */ - - if ((tt_scsi_dma.dma_ctrl & 2) == 0) - { - atari_enable_irq(IRQ_TT_MFP_SCSI); - return IRQ_HANDLED; - } - - if (dma_cnt == 0) - { - printk(KERN_NOTICE "DMA emulation: count is zero.\n"); - tt_scsi_dma.dma_ctrl &= 0xfd; /* DMA ready. */ - atari_enable_irq(IRQ_TT_MFP_SCSI); - return IRQ_HANDLED; - } - - /* - * Install new bus error routine. - */ - - __asm__ __volatile__ ("movec.l %%vbr,%%a0\n\t" - "move.l 8(%%a0),%0\n\t" - "move.l %1,8(%%a0)\n\t" - : "=&r" (save_buserr) - : "r" (&&scsi_bus_error) - : "a0" ); - - hades_dma_ctrl &= 0xfc; /* Bus error and EOP off. */ - - /* - * Save the stack pointer. - */ - - __asm__ __volatile__ ("move.l %%sp,%0\n\t" - : "=&r" (save_sp) ); - - tries = 100; /* Maximum number of bus errors. */ - start_addr = phys_to_virt(dma_base); - end_addr = start_addr + dma_cnt; - -scsi_loop: - dma_cnt--; - rem = dma_cnt & (TRANSFER_SIZE - 1); - dma_cnt &= ~(TRANSFER_SIZE - 1); - psdm_reg = &hades_psdm_reg; - - if (tt_scsi_dma.dma_ctrl & 1) /* Read or write? */ - { - /* - * SCSI write. Abort when count is zero. - */ - - switch (rem) - { - case 0: - while (dma_cnt > 0) - { - dma_cnt -= TRANSFER_SIZE; - - *psdm_reg = *start_addr++; - case 15: - *psdm_reg = *start_addr++; - case 14: - *psdm_reg = *start_addr++; - case 13: - *psdm_reg = *start_addr++; - case 12: - *psdm_reg = *start_addr++; - case 11: - *psdm_reg = *start_addr++; - case 10: - *psdm_reg = *start_addr++; - case 9: - *psdm_reg = *start_addr++; - case 8: - *psdm_reg = *start_addr++; - case 7: - *psdm_reg = *start_addr++; - case 6: - *psdm_reg = *start_addr++; - case 5: - *psdm_reg = *start_addr++; - case 4: - *psdm_reg = *start_addr++; - case 3: - *psdm_reg = *start_addr++; - case 2: - *psdm_reg = *start_addr++; - case 1: - *psdm_reg = *start_addr++; - } - } - - hades_dma_ctrl |= 1; /* Set EOP. */ - udelay(10); - *psdm_reg = *start_addr++; /* Dummy byte. */ - tt_scsi_dma.dma_ctrl &= 0xfd; /* DMA ready. */ - } - else - { - /* - * SCSI read. Abort when count is zero. - */ - - switch (rem) - { - case 0: - while (dma_cnt > 0) - { - dma_cnt -= TRANSFER_SIZE; - - *start_addr++ = *psdm_reg; - case 15: - *start_addr++ = *psdm_reg; - case 14: - *start_addr++ = *psdm_reg; - case 13: - *start_addr++ = *psdm_reg; - case 12: - *start_addr++ = *psdm_reg; - case 11: - *start_addr++ = *psdm_reg; - case 10: - *start_addr++ = *psdm_reg; - case 9: - *start_addr++ = *psdm_reg; - case 8: - *start_addr++ = *psdm_reg; - case 7: - *start_addr++ = *psdm_reg; - case 6: - *start_addr++ = *psdm_reg; - case 5: - *start_addr++ = *psdm_reg; - case 4: - *start_addr++ = *psdm_reg; - case 3: - *start_addr++ = *psdm_reg; - case 2: - *start_addr++ = *psdm_reg; - case 1: - *start_addr++ = *psdm_reg; - } - } - - hades_dma_ctrl |= 1; /* Set EOP. */ - udelay(10); - *start_addr++ = *psdm_reg; - tt_scsi_dma.dma_ctrl &= 0xfd; /* DMA ready. */ - - set_restdata_reg(start_addr); - } - - if (start_addr != end_addr) - printk(KERN_CRIT "DMA emulation: FATAL: Count is not zero at end of transfer.\n"); - - dma_cnt = end_addr - start_addr; - -scsi_end: - dma_base = (dma_cnt == 0) ? virt_to_phys(start_addr - 1) + 1 : - virt_to_phys(start_addr); - - SCSI_DMA_WRITE_P(dma_addr, dma_base); - SCSI_DMA_WRITE_P(dma_cnt, dma_cnt); - - /* - * Restore old bus error routine. - */ - - __asm__ __volatile__ ("movec.l %%vbr,%%a0\n\t" - "move.l %0,8(%%a0)\n\t" - : - : "r" (save_buserr) - : "a0" ); - - atari_enable_irq(IRQ_TT_MFP_SCSI); - - return IRQ_HANDLED; - -scsi_bus_error: - /* - * First check if the bus error is caused by our code. - * If not, call the original handler. - */ - - __asm__ __volatile__ ("cmp.l %0,2(%%sp)\n\t" - "bcs.s .old_vector\n\t" - "cmp.l %1,2(%%sp)\n\t" - "bls.s .scsi_buserr\n" - ".old_vector:\n\t" - "move.l %2,-(%%sp)\n\t" - "rts\n" - ".scsi_buserr:\n\t" - : - : "i" (&&scsi_loop), "i" (&&scsi_end), - "m" (save_buserr) ); - - if (CPU_IS_060) - { - /* - * Get effective address and restore the stack. - */ - - __asm__ __volatile__ ("move.l 8(%%sp),%0\n\t" - "move.l %1,%%sp\n\t" - : "=a&" (eff_addr) - : "r" (save_sp) ); - } - else - { - register struct m68040_frame *frame; - - __asm__ __volatile__ ("lea 8(%%sp),%0\n\t" - : "=a&" (frame) ); - - if (tt_scsi_dma.dma_ctrl & 1) - { - /* - * Bus error while writing. - */ - - if (frame->wb3s & WBV_040) - { - if (frame->wb3a == (long) &hades_psdm_reg) - start_addr--; - else - writeback(frame->wb3s, frame->wb3a, - frame->wb3d, &&scsi_bus_error); - } - - if (frame->wb2s & WBV_040) - { - if (frame->wb2a == (long) &hades_psdm_reg) - start_addr--; - else - writeback(frame->wb2s, frame->wb2a, - frame->wb2d, &&scsi_bus_error); - } - - if (frame->wb1s & WBV_040) - { - if (frame->wb1a == (long) &hades_psdm_reg) - start_addr--; - } - } - else - { - /* - * Bus error while reading. - */ - - if (frame->wb3s & WBV_040) - writeback(frame->wb3s, frame->wb3a, - frame->wb3d, &&scsi_bus_error); - } - - eff_addr = (unsigned char *) frame->faddr; - - __asm__ __volatile__ ("move.l %0,%%sp\n\t" - : - : "r" (save_sp) ); - } - - dma_cnt = end_addr - start_addr; - - if (eff_addr == &hades_psdm_reg) - { - /* - * Bus error occurred while reading the pseudo - * DMA register. Time out. - */ - - tries--; - - if (tries <= 0) - { - if ((tt_scsi_dma.dma_ctrl & 1) == 0) /* Read or write? */ - set_restdata_reg(start_addr); - - if (dma_cnt <= 1) - printk(KERN_CRIT "DMA emulation: Fatal " - "error while %s the last byte.\n", - (tt_scsi_dma.dma_ctrl & 1) - ? "writing" : "reading"); - - goto scsi_end; - } - else - goto scsi_loop; - } - else - { - /* - * Bus error during pseudo DMA transfer. - * Terminate the DMA transfer. - */ - - hades_dma_ctrl |= 3; /* Set EOP and bus error. */ - if ((tt_scsi_dma.dma_ctrl & 1) == 0) /* Read or write? */ - set_restdata_reg(start_addr); - goto scsi_end; - } -} diff --git a/drivers/scsi/atari_scsi.c b/drivers/scsi/atari_scsi.c index f5732d8f67fe..21fe07f9df87 100644 --- a/drivers/scsi/atari_scsi.c +++ b/drivers/scsi/atari_scsi.c @@ -249,10 +249,6 @@ static int setup_hostid = -1; module_param(setup_hostid, int, 0); -#if defined(CONFIG_TT_DMA_EMUL) -#include "atari_dma_emul.c" -#endif - #if defined(REAL_DMA) static int scsi_dma_is_ignored_buserr(unsigned char dma_stat) @@ -695,21 +691,8 @@ int atari_scsi_detect(struct scsi_host_template *host) #ifdef REAL_DMA tt_scsi_dma.dma_ctrl = 0; atari_dma_residual = 0; -#ifdef CONFIG_TT_DMA_EMUL - if (MACH_IS_HADES) { - if (request_irq(IRQ_AUTO_2, hades_dma_emulator, - IRQ_TYPE_PRIO, "Hades DMA emulator", - hades_dma_emulator)) { - printk(KERN_ERR "atari_scsi_detect: cannot allocate irq %d, aborting (MACH_IS_HADES)",IRQ_AUTO_2); - free_irq(IRQ_TT_MFP_SCSI, instance); - scsi_unregister(atari_scsi_host); - atari_stram_free(atari_dma_buffer); - atari_dma_buffer = 0; - return 0; - } - } -#endif - if (MACH_IS_MEDUSA || MACH_IS_HADES) { + + if (MACH_IS_MEDUSA) { /* While the read overruns (described by Drew Eckhardt in * NCR5380.c) never happened on TTs, they do in fact on the Medusa * (This was the cause why SCSI didn't work right for so long @@ -1007,11 +990,7 @@ static unsigned long atari_dma_xfer_len(unsigned long wanted_len, Scsi_Cmnd *cmd, int write_flag) { unsigned long possible_len, limit; -#ifndef CONFIG_TT_DMA_EMUL - if (MACH_IS_HADES) - /* Hades has no SCSI DMA at all :-( Always force use of PIO */ - return 0; -#endif + if (IS_A_TT()) /* TT SCSI DMA can transfer arbitrary #bytes */ return wanted_len; diff --git a/drivers/serial/8250.c b/drivers/serial/8250.c index d4104a3bbe87..d3ca7d32abe0 100644 --- a/drivers/serial/8250.c +++ b/drivers/serial/8250.c @@ -2969,6 +2969,9 @@ static int __init serial8250_init(void) "%d ports, IRQ sharing %sabled\n", nr_uarts, share_irqs ? "en" : "dis"); + for (i = 0; i < NR_IRQS; i++) + spin_lock_init(&irq_lists[i].lock); + #ifdef CONFIG_SPARC ret = sunserial_register_minors(&serial8250_reg, UART_NR); #else diff --git a/drivers/video/console/fbcon.c b/drivers/video/console/fbcon.c index 9cbff84b787d..da91bb16da8a 100644 --- a/drivers/video/console/fbcon.c +++ b/drivers/video/console/fbcon.c @@ -1855,8 +1855,6 @@ static int fbcon_scroll(struct vc_data *vc, int t, int b, int dir, struct fb_info *info = registered_fb[con2fb_map[vc->vc_num]]; struct display *p = &fb_display[vc->vc_num]; int scroll_partial = info->flags & FBINFO_PARTIAL_PAN_OK; - unsigned short saved_ec; - int ret; if (fbcon_is_inactive(vc, info)) return -EINVAL; @@ -1869,11 +1867,6 @@ static int fbcon_scroll(struct vc_data *vc, int t, int b, int dir, * whole screen (prevents flicker). */ - saved_ec = vc->vc_video_erase_char; - vc->vc_video_erase_char = vc->vc_scrl_erase_char; - - ret = 0; - switch (dir) { case SM_UP: if (count > vc->vc_rows) /* Maximum realistic size */ @@ -1890,9 +1883,9 @@ static int fbcon_scroll(struct vc_data *vc, int t, int b, int dir, scr_memsetw((unsigned short *) (vc->vc_origin + vc->vc_size_row * (b - count)), - vc->vc_scrl_erase_char, + vc->vc_video_erase_char, vc->vc_size_row * count); - ret = 1; + return 1; break; case SCROLL_WRAP_MOVE: @@ -1962,10 +1955,9 @@ static int fbcon_scroll(struct vc_data *vc, int t, int b, int dir, scr_memsetw((unsigned short *) (vc->vc_origin + vc->vc_size_row * (b - count)), - vc->vc_scrl_erase_char, + vc->vc_video_erase_char, vc->vc_size_row * count); - ret = 1; - break; + return 1; } break; @@ -1982,9 +1974,9 @@ static int fbcon_scroll(struct vc_data *vc, int t, int b, int dir, scr_memsetw((unsigned short *) (vc->vc_origin + vc->vc_size_row * t), - vc->vc_scrl_erase_char, + vc->vc_video_erase_char, vc->vc_size_row * count); - ret = 1; + return 1; break; case SCROLL_WRAP_MOVE: @@ -2052,15 +2044,12 @@ static int fbcon_scroll(struct vc_data *vc, int t, int b, int dir, scr_memsetw((unsigned short *) (vc->vc_origin + vc->vc_size_row * t), - vc->vc_scrl_erase_char, + vc->vc_video_erase_char, vc->vc_size_row * count); - ret = 1; - break; + return 1; } - break; } - vc->vc_video_erase_char = saved_ec; - return ret; + return 0; } @@ -2522,9 +2511,6 @@ static int fbcon_do_set_font(struct vc_data *vc, int w, int h, c = vc->vc_video_erase_char; vc->vc_video_erase_char = ((c & 0xfe00) >> 1) | (c & 0xff); - c = vc->vc_scrl_erase_char; - vc->vc_scrl_erase_char = - ((c & 0xFE00) >> 1) | (c & 0xFF); vc->vc_attr >>= 1; } } else if (!vc->vc_hi_font_mask && cnt == 512) { @@ -2555,14 +2541,9 @@ static int fbcon_do_set_font(struct vc_data *vc, int w, int h, if (vc->vc_can_do_color) { vc->vc_video_erase_char = ((c & 0xff00) << 1) | (c & 0xff); - c = vc->vc_scrl_erase_char; - vc->vc_scrl_erase_char = - ((c & 0xFF00) << 1) | (c & 0xFF); vc->vc_attr <<= 1; - } else { + } else vc->vc_video_erase_char = c & ~0x100; - vc->vc_scrl_erase_char = c & ~0x100; - } } } diff --git a/drivers/video/console/mdacon.c b/drivers/video/console/mdacon.c index 9901064199bd..dd3eaaad4441 100644 --- a/drivers/video/console/mdacon.c +++ b/drivers/video/console/mdacon.c @@ -533,7 +533,7 @@ static void mdacon_cursor(struct vc_data *c, int mode) static int mdacon_scroll(struct vc_data *c, int t, int b, int dir, int lines) { - u16 eattr = mda_convert_attr(c->vc_scrl_erase_char); + u16 eattr = mda_convert_attr(c->vc_video_erase_char); if (!lines) return 0; diff --git a/drivers/video/console/sticon.c b/drivers/video/console/sticon.c index 4055dbdd1b42..491c1c1baf4c 100644 --- a/drivers/video/console/sticon.c +++ b/drivers/video/console/sticon.c @@ -170,12 +170,12 @@ static int sticon_scroll(struct vc_data *conp, int t, int b, int dir, int count) switch (dir) { case SM_UP: sti_bmove(sti, t + count, 0, t, 0, b - t - count, conp->vc_cols); - sti_clear(sti, b - count, 0, count, conp->vc_cols, conp->vc_scrl_erase_char); + sti_clear(sti, b - count, 0, count, conp->vc_cols, conp->vc_video_erase_char); break; case SM_DOWN: sti_bmove(sti, t, 0, t + count, 0, b - t - count, conp->vc_cols); - sti_clear(sti, t, 0, count, conp->vc_cols, conp->vc_scrl_erase_char); + sti_clear(sti, t, 0, count, conp->vc_cols, conp->vc_video_erase_char); break; } diff --git a/drivers/video/console/vgacon.c b/drivers/video/console/vgacon.c index bd1f57b259d9..6df29a62d720 100644 --- a/drivers/video/console/vgacon.c +++ b/drivers/video/console/vgacon.c @@ -1350,7 +1350,7 @@ static int vgacon_scroll(struct vc_data *c, int t, int b, int dir, } else c->vc_origin += delta; scr_memsetw((u16 *) (c->vc_origin + c->vc_screenbuf_size - - delta), c->vc_scrl_erase_char, + delta), c->vc_video_erase_char, delta); } else { if (oldo - delta < vga_vram_base) { @@ -1363,7 +1363,7 @@ static int vgacon_scroll(struct vc_data *c, int t, int b, int dir, } else c->vc_origin -= delta; c->vc_scr_end = c->vc_origin + c->vc_screenbuf_size; - scr_memsetw((u16 *) (c->vc_origin), c->vc_scrl_erase_char, + scr_memsetw((u16 *) (c->vc_origin), c->vc_video_erase_char, delta); } c->vc_scr_end = c->vc_origin + c->vc_screenbuf_size; diff --git a/fs/Kconfig b/fs/Kconfig index f54a157a0296..9e9d70c02a07 100644 --- a/fs/Kconfig +++ b/fs/Kconfig @@ -220,17 +220,16 @@ config JBD tristate help This is a generic journalling layer for block devices. It is - currently used by the ext3 and OCFS2 file systems, but it could - also be used to add journal support to other file systems or block + currently used by the ext3 file system, but it could also be + used to add journal support to other file systems or block devices such as RAID or LVM. - If you are using the ext3 or OCFS2 file systems, you need to - say Y here. If you are not using ext3 OCFS2 then you will probably - want to say N. + If you are using the ext3 file system, you need to say Y here. + If you are not using ext3 then you will probably want to say N. To compile this device as a module, choose M here: the module will be - called jbd. If you are compiling ext3 or OCFS2 into the kernel, - you cannot compile this code as a module. + called jbd. If you are compiling ext3 into the kernel, you + cannot compile this code as a module. config JBD_DEBUG bool "JBD (ext3) debugging support" @@ -254,15 +253,16 @@ config JBD2 help This is a generic journaling layer for block devices that support both 32-bit and 64-bit block numbers. It is currently used by - the ext4 filesystem, but it could also be used to add + the ext4 and OCFS2 filesystems, but it could also be used to add journal support to other file systems or block devices such as RAID or LVM. - If you are using ext4, you need to say Y here. If you are not - using ext4 then you will probably want to say N. + If you are using ext4 or OCFS2, you need to say Y here. + If you are not using ext4 or OCFS2 then you will + probably want to say N. To compile this device as a module, choose M here. The module will be - called jbd2. If you are compiling ext4 into the kernel, + called jbd2. If you are compiling ext4 or OCFS2 into the kernel, you cannot compile this code as a module. config JBD2_DEBUG @@ -433,6 +433,14 @@ config FS_POSIX_ACL bool default n +config FILE_LOCKING + bool "Enable POSIX file locking API" if EMBEDDED + default y + help + This option enables standard file locking support, required + for filesystems like NFS and for the flock() system + call. Disabling this option saves about 11k. + source "fs/xfs/Kconfig" source "fs/gfs2/Kconfig" @@ -440,7 +448,7 @@ config OCFS2_FS tristate "OCFS2 file system support" depends on NET && SYSFS select CONFIGFS_FS - select JBD + select JBD2 select CRC32 help OCFS2 is a general purpose extent based shared disk cluster file @@ -511,6 +519,16 @@ config OCFS2_DEBUG_FS this option for debugging only as it is likely to decrease performance of the filesystem. +config OCFS2_COMPAT_JBD + bool "Use JBD for compatibility" + depends on OCFS2_FS + default n + select JBD + help + The ocfs2 filesystem now uses JBD2 for its journalling. JBD2 + is backwards compatible with JBD. It is safe to say N here. + However, if you really want to use the original JBD, say Y here. + endif # BLOCK config DNOTIFY @@ -1779,6 +1797,28 @@ config SUNRPC_XPRT_RDMA If unsure, say N. +config SUNRPC_REGISTER_V4 + bool "Register local RPC services via rpcbind v4 (EXPERIMENTAL)" + depends on SUNRPC && EXPERIMENTAL + default n + help + Sun added support for registering RPC services at an IPv6 + address by creating two new versions of the rpcbind protocol + (RFC 1833). + + This option enables support in the kernel RPC server for + registering kernel RPC services via version 4 of the rpcbind + protocol. If you enable this option, you must run a portmapper + daemon that supports rpcbind protocol version 4. + + Serving NFS over IPv6 from knfsd (the kernel's NFS server) + requires that you enable this option and use a portmapper that + supports rpcbind version 4. + + If unsure, say N to get traditional behavior (register kernel + RPC services using only rpcbind version 2). Distributions + using the legacy Linux portmapper daemon must say N here. + config RPCSEC_GSS_KRB5 tristate "Secure RPC: Kerberos V mechanism (EXPERIMENTAL)" depends on SUNRPC && EXPERIMENTAL diff --git a/fs/Makefile b/fs/Makefile index de404b00eb0c..b6f27dc26b72 100644 --- a/fs/Makefile +++ b/fs/Makefile @@ -7,7 +7,7 @@ obj-y := open.o read_write.o file_table.o super.o \ char_dev.o stat.o exec.o pipe.o namei.o fcntl.o \ - ioctl.o readdir.o select.o fifo.o locks.o dcache.o inode.o \ + ioctl.o readdir.o select.o fifo.o dcache.o inode.o \ attr.o bad_inode.o file.o filesystems.o namespace.o aio.o \ seq_file.o xattr.o libfs.o fs-writeback.o \ pnode.o drop_caches.o splice.o sync.o utimes.o \ @@ -27,6 +27,7 @@ obj-$(CONFIG_ANON_INODES) += anon_inodes.o obj-$(CONFIG_SIGNALFD) += signalfd.o obj-$(CONFIG_TIMERFD) += timerfd.o obj-$(CONFIG_EVENTFD) += eventfd.o +obj-$(CONFIG_FILE_LOCKING) += locks.o obj-$(CONFIG_COMPAT) += compat.o compat_ioctl.o nfsd-$(CONFIG_NFSD) := nfsctl.o diff --git a/fs/lockd/Makefile b/fs/lockd/Makefile index 7725a0a9a555..97f6073ab339 100644 --- a/fs/lockd/Makefile +++ b/fs/lockd/Makefile @@ -5,6 +5,6 @@ obj-$(CONFIG_LOCKD) += lockd.o lockd-objs-y := clntlock.o clntproc.o host.o svc.o svclock.o svcshare.o \ - svcproc.o svcsubs.o mon.o xdr.o + svcproc.o svcsubs.o mon.o xdr.o grace.o lockd-objs-$(CONFIG_LOCKD_V4) += xdr4.o svc4proc.o lockd-objs := $(lockd-objs-y) diff --git a/fs/lockd/clntlock.c b/fs/lockd/clntlock.c index 0b45fd3a4bfd..8307dd64bf46 100644 --- a/fs/lockd/clntlock.c +++ b/fs/lockd/clntlock.c @@ -54,14 +54,13 @@ struct nlm_host *nlmclnt_init(const struct nlmclnt_initdata *nlm_init) u32 nlm_version = (nlm_init->nfs_version == 2) ? 1 : 4; int status; - status = lockd_up(nlm_init->protocol); + status = lockd_up(); if (status < 0) return ERR_PTR(status); - host = nlmclnt_lookup_host((struct sockaddr_in *)nlm_init->address, + host = nlmclnt_lookup_host(nlm_init->address, nlm_init->addrlen, nlm_init->protocol, nlm_version, - nlm_init->hostname, - strlen(nlm_init->hostname)); + nlm_init->hostname); if (host == NULL) { lockd_down(); return ERR_PTR(-ENOLCK); @@ -142,7 +141,7 @@ int nlmclnt_block(struct nlm_wait *block, struct nlm_rqst *req, long timeout) /* * The server lockd has called us back to tell us the lock was granted */ -__be32 nlmclnt_grant(const struct sockaddr_in *addr, const struct nlm_lock *lock) +__be32 nlmclnt_grant(const struct sockaddr *addr, const struct nlm_lock *lock) { const struct file_lock *fl = &lock->fl; const struct nfs_fh *fh = &lock->fh; @@ -166,7 +165,7 @@ __be32 nlmclnt_grant(const struct sockaddr_in *addr, const struct nlm_lock *lock */ if (fl_blocked->fl_u.nfs_fl.owner->pid != lock->svid) continue; - if (!nlm_cmp_addr(&block->b_host->h_addr, addr)) + if (!nlm_cmp_addr(nlm_addr(block->b_host), addr)) continue; if (nfs_compare_fh(NFS_FH(fl_blocked->fl_file->f_path.dentry->d_inode) ,fh) != 0) continue; @@ -216,7 +215,7 @@ reclaimer(void *ptr) /* This one ensures that our parent doesn't terminate while the * reclaim is in progress */ lock_kernel(); - lockd_up(0); /* note: this cannot fail as lockd is already running */ + lockd_up(); /* note: this cannot fail as lockd is already running */ dprintk("lockd: reclaiming locks for host %s\n", host->h_name); diff --git a/fs/lockd/grace.c b/fs/lockd/grace.c new file mode 100644 index 000000000000..183cc1f0af1c --- /dev/null +++ b/fs/lockd/grace.c @@ -0,0 +1,59 @@ +/* + * Common code for control of lockd and nfsv4 grace periods. + */ + +#include <linux/module.h> +#include <linux/lockd/bind.h> + +static LIST_HEAD(grace_list); +static DEFINE_SPINLOCK(grace_lock); + +/** + * locks_start_grace + * @lm: who this grace period is for + * + * A grace period is a period during which locks should not be given + * out. Currently grace periods are only enforced by the two lock + * managers (lockd and nfsd), using the locks_in_grace() function to + * check when they are in a grace period. + * + * This function is called to start a grace period. + */ +void locks_start_grace(struct lock_manager *lm) +{ + spin_lock(&grace_lock); + list_add(&lm->list, &grace_list); + spin_unlock(&grace_lock); +} +EXPORT_SYMBOL_GPL(locks_start_grace); + +/** + * locks_end_grace + * @lm: who this grace period is for + * + * Call this function to state that the given lock manager is ready to + * resume regular locking. The grace period will not end until all lock + * managers that called locks_start_grace() also call locks_end_grace(). + * Note that callers count on it being safe to call this more than once, + * and the second call should be a no-op. + */ +void locks_end_grace(struct lock_manager *lm) +{ + spin_lock(&grace_lock); + list_del_init(&lm->list); + spin_unlock(&grace_lock); +} +EXPORT_SYMBOL_GPL(locks_end_grace); + +/** + * locks_in_grace + * + * Lock managers call this function to determine when it is OK for them + * to answer ordinary lock requests, and when they should accept only + * lock reclaims. + */ +int locks_in_grace(void) +{ + return !list_empty(&grace_list); +} +EXPORT_SYMBOL_GPL(locks_in_grace); diff --git a/fs/lockd/host.c b/fs/lockd/host.c index a17664c7eacc..9fd8889097b7 100644 --- a/fs/lockd/host.c +++ b/fs/lockd/host.c @@ -11,16 +11,17 @@ #include <linux/types.h> #include <linux/slab.h> #include <linux/in.h> +#include <linux/in6.h> #include <linux/sunrpc/clnt.h> #include <linux/sunrpc/svc.h> #include <linux/lockd/lockd.h> #include <linux/lockd/sm_inter.h> #include <linux/mutex.h> +#include <net/ipv6.h> #define NLMDBG_FACILITY NLMDBG_HOSTCACHE #define NLM_HOST_NRHASH 32 -#define NLM_ADDRHASH(addr) (ntohl(addr) & (NLM_HOST_NRHASH-1)) #define NLM_HOST_REBIND (60 * HZ) #define NLM_HOST_EXPIRE (300 * HZ) #define NLM_HOST_COLLECT (120 * HZ) @@ -30,42 +31,115 @@ static unsigned long next_gc; static int nrhosts; static DEFINE_MUTEX(nlm_host_mutex); - static void nlm_gc_hosts(void); -static struct nsm_handle * __nsm_find(const struct sockaddr_in *, - const char *, unsigned int, int); -static struct nsm_handle * nsm_find(const struct sockaddr_in *sin, - const char *hostname, - unsigned int hostname_len); +static struct nsm_handle *nsm_find(const struct sockaddr *sap, + const size_t salen, + const char *hostname, + const size_t hostname_len, + const int create); + +struct nlm_lookup_host_info { + const int server; /* search for server|client */ + const struct sockaddr *sap; /* address to search for */ + const size_t salen; /* it's length */ + const unsigned short protocol; /* transport to search for*/ + const u32 version; /* NLM version to search for */ + const char *hostname; /* remote's hostname */ + const size_t hostname_len; /* it's length */ + const struct sockaddr *src_sap; /* our address (optional) */ + const size_t src_len; /* it's length */ +}; + +/* + * Hash function must work well on big- and little-endian platforms + */ +static unsigned int __nlm_hash32(const __be32 n) +{ + unsigned int hash = (__force u32)n ^ ((__force u32)n >> 16); + return hash ^ (hash >> 8); +} + +static unsigned int __nlm_hash_addr4(const struct sockaddr *sap) +{ + const struct sockaddr_in *sin = (struct sockaddr_in *)sap; + return __nlm_hash32(sin->sin_addr.s_addr); +} + +static unsigned int __nlm_hash_addr6(const struct sockaddr *sap) +{ + const struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *)sap; + const struct in6_addr addr = sin6->sin6_addr; + return __nlm_hash32(addr.s6_addr32[0]) ^ + __nlm_hash32(addr.s6_addr32[1]) ^ + __nlm_hash32(addr.s6_addr32[2]) ^ + __nlm_hash32(addr.s6_addr32[3]); +} + +static unsigned int nlm_hash_address(const struct sockaddr *sap) +{ + unsigned int hash; + + switch (sap->sa_family) { + case AF_INET: + hash = __nlm_hash_addr4(sap); + break; + case AF_INET6: + hash = __nlm_hash_addr6(sap); + break; + default: + hash = 0; + } + return hash & (NLM_HOST_NRHASH - 1); +} + +static void nlm_clear_port(struct sockaddr *sap) +{ + switch (sap->sa_family) { + case AF_INET: + ((struct sockaddr_in *)sap)->sin_port = 0; + break; + case AF_INET6: + ((struct sockaddr_in6 *)sap)->sin6_port = 0; + break; + } +} + +static void nlm_display_address(const struct sockaddr *sap, + char *buf, const size_t len) +{ + const struct sockaddr_in *sin = (struct sockaddr_in *)sap; + const struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *)sap; + + switch (sap->sa_family) { + case AF_UNSPEC: + snprintf(buf, len, "unspecified"); + break; + case AF_INET: + snprintf(buf, len, NIPQUAD_FMT, NIPQUAD(sin->sin_addr.s_addr)); + break; + case AF_INET6: + if (ipv6_addr_v4mapped(&sin6->sin6_addr)) + snprintf(buf, len, NIPQUAD_FMT, + NIPQUAD(sin6->sin6_addr.s6_addr32[3])); + else + snprintf(buf, len, NIP6_FMT, NIP6(sin6->sin6_addr)); + break; + default: + snprintf(buf, len, "unsupported address family"); + break; + } +} /* * Common host lookup routine for server & client */ -static struct nlm_host *nlm_lookup_host(int server, - const struct sockaddr_in *sin, - int proto, u32 version, - const char *hostname, - unsigned int hostname_len, - const struct sockaddr_in *ssin) +static struct nlm_host *nlm_lookup_host(struct nlm_lookup_host_info *ni) { struct hlist_head *chain; struct hlist_node *pos; struct nlm_host *host; struct nsm_handle *nsm = NULL; - int hash; - - dprintk("lockd: nlm_lookup_host("NIPQUAD_FMT"->"NIPQUAD_FMT - ", p=%d, v=%u, my role=%s, name=%.*s)\n", - NIPQUAD(ssin->sin_addr.s_addr), - NIPQUAD(sin->sin_addr.s_addr), proto, version, - server? "server" : "client", - hostname_len, - hostname? hostname : "<none>"); - - hash = NLM_ADDRHASH(sin->sin_addr.s_addr); - - /* Lock hash table */ mutex_lock(&nlm_host_mutex); if (time_after_eq(jiffies, next_gc)) @@ -78,22 +152,22 @@ static struct nlm_host *nlm_lookup_host(int server, * different NLM rpc_clients into one single nlm_host object. * This would allow us to have one nlm_host per address. */ - chain = &nlm_hosts[hash]; + chain = &nlm_hosts[nlm_hash_address(ni->sap)]; hlist_for_each_entry(host, pos, chain, h_hash) { - if (!nlm_cmp_addr(&host->h_addr, sin)) + if (!nlm_cmp_addr(nlm_addr(host), ni->sap)) continue; /* See if we have an NSM handle for this client */ if (!nsm) nsm = host->h_nsmhandle; - if (host->h_proto != proto) + if (host->h_proto != ni->protocol) continue; - if (host->h_version != version) + if (host->h_version != ni->version) continue; - if (host->h_server != server) + if (host->h_server != ni->server) continue; - if (!nlm_cmp_addr(&host->h_saddr, ssin)) + if (!nlm_cmp_addr(nlm_srcaddr(host), ni->src_sap)) continue; /* Move to head of hash chain. */ @@ -101,30 +175,41 @@ static struct nlm_host *nlm_lookup_host(int server, hlist_add_head(&host->h_hash, chain); nlm_get_host(host); + dprintk("lockd: nlm_lookup_host found host %s (%s)\n", + host->h_name, host->h_addrbuf); goto out; } - if (nsm) - atomic_inc(&nsm->sm_count); - - host = NULL; - /* Sadly, the host isn't in our hash table yet. See if - * we have an NSM handle for it. If not, create one. + /* + * The host wasn't in our hash table. If we don't + * have an NSM handle for it yet, create one. */ - if (!nsm && !(nsm = nsm_find(sin, hostname, hostname_len))) - goto out; + if (nsm) + atomic_inc(&nsm->sm_count); + else { + host = NULL; + nsm = nsm_find(ni->sap, ni->salen, + ni->hostname, ni->hostname_len, 1); + if (!nsm) { + dprintk("lockd: nlm_lookup_host failed; " + "no nsm handle\n"); + goto out; + } + } host = kzalloc(sizeof(*host), GFP_KERNEL); if (!host) { nsm_release(nsm); + dprintk("lockd: nlm_lookup_host failed; no memory\n"); goto out; } host->h_name = nsm->sm_name; - host->h_addr = *sin; - host->h_addr.sin_port = 0; /* ouch! */ - host->h_saddr = *ssin; - host->h_version = version; - host->h_proto = proto; + memcpy(nlm_addr(host), ni->sap, ni->salen); + host->h_addrlen = ni->salen; + nlm_clear_port(nlm_addr(host)); + memcpy(nlm_srcaddr(host), ni->src_sap, ni->src_len); + host->h_version = ni->version; + host->h_proto = ni->protocol; host->h_rpcclnt = NULL; mutex_init(&host->h_mutex); host->h_nextrebind = jiffies + NLM_HOST_REBIND; @@ -135,7 +220,7 @@ static struct nlm_host *nlm_lookup_host(int server, host->h_state = 0; /* pseudo NSM state */ host->h_nsmstate = 0; /* real NSM state */ host->h_nsmhandle = nsm; - host->h_server = server; + host->h_server = ni->server; hlist_add_head(&host->h_hash, chain); INIT_LIST_HEAD(&host->h_lockowners); spin_lock_init(&host->h_lock); @@ -143,6 +228,15 @@ static struct nlm_host *nlm_lookup_host(int server, INIT_LIST_HEAD(&host->h_reclaim); nrhosts++; + + nlm_display_address((struct sockaddr *)&host->h_addr, + host->h_addrbuf, sizeof(host->h_addrbuf)); + nlm_display_address((struct sockaddr *)&host->h_srcaddr, + host->h_srcaddrbuf, sizeof(host->h_srcaddrbuf)); + + dprintk("lockd: nlm_lookup_host created host %s\n", + host->h_name); + out: mutex_unlock(&nlm_host_mutex); return host; @@ -170,33 +264,103 @@ nlm_destroy_host(struct nlm_host *host) kfree(host); } -/* - * Find an NLM server handle in the cache. If there is none, create it. +/** + * nlmclnt_lookup_host - Find an NLM host handle matching a remote server + * @sap: network address of server + * @salen: length of server address + * @protocol: transport protocol to use + * @version: NLM protocol version + * @hostname: '\0'-terminated hostname of server + * + * Returns an nlm_host structure that matches the passed-in + * [server address, transport protocol, NLM version, server hostname]. + * If one doesn't already exist in the host cache, a new handle is + * created and returned. */ -struct nlm_host *nlmclnt_lookup_host(const struct sockaddr_in *sin, - int proto, u32 version, - const char *hostname, - unsigned int hostname_len) +struct nlm_host *nlmclnt_lookup_host(const struct sockaddr *sap, + const size_t salen, + const unsigned short protocol, + const u32 version, const char *hostname) { - struct sockaddr_in ssin = {0}; - - return nlm_lookup_host(0, sin, proto, version, - hostname, hostname_len, &ssin); + const struct sockaddr source = { + .sa_family = AF_UNSPEC, + }; + struct nlm_lookup_host_info ni = { + .server = 0, + .sap = sap, + .salen = salen, + .protocol = protocol, + .version = version, + .hostname = hostname, + .hostname_len = strlen(hostname), + .src_sap = &source, + .src_len = sizeof(source), + }; + + dprintk("lockd: %s(host='%s', vers=%u, proto=%s)\n", __func__, + (hostname ? hostname : "<none>"), version, + (protocol == IPPROTO_UDP ? "udp" : "tcp")); + + return nlm_lookup_host(&ni); } -/* - * Find an NLM client handle in the cache. If there is none, create it. +/** + * nlmsvc_lookup_host - Find an NLM host handle matching a remote client + * @rqstp: incoming NLM request + * @hostname: name of client host + * @hostname_len: length of client hostname + * + * Returns an nlm_host structure that matches the [client address, + * transport protocol, NLM version, client hostname] of the passed-in + * NLM request. If one doesn't already exist in the host cache, a + * new handle is created and returned. + * + * Before possibly creating a new nlm_host, construct a sockaddr + * for a specific source address in case the local system has + * multiple network addresses. The family of the address in + * rq_daddr is guaranteed to be the same as the family of the + * address in rq_addr, so it's safe to use the same family for + * the source address. */ -struct nlm_host * -nlmsvc_lookup_host(struct svc_rqst *rqstp, - const char *hostname, unsigned int hostname_len) +struct nlm_host *nlmsvc_lookup_host(const struct svc_rqst *rqstp, + const char *hostname, + const size_t hostname_len) { - struct sockaddr_in ssin = {0}; + struct sockaddr_in sin = { + .sin_family = AF_INET, + }; + struct sockaddr_in6 sin6 = { + .sin6_family = AF_INET6, + }; + struct nlm_lookup_host_info ni = { + .server = 1, + .sap = svc_addr(rqstp), + .salen = rqstp->rq_addrlen, + .protocol = rqstp->rq_prot, + .version = rqstp->rq_vers, + .hostname = hostname, + .hostname_len = hostname_len, + .src_len = rqstp->rq_addrlen, + }; + + dprintk("lockd: %s(host='%*s', vers=%u, proto=%s)\n", __func__, + (int)hostname_len, hostname, rqstp->rq_vers, + (rqstp->rq_prot == IPPROTO_UDP ? "udp" : "tcp")); + + switch (ni.sap->sa_family) { + case AF_INET: + sin.sin_addr.s_addr = rqstp->rq_daddr.addr.s_addr; + ni.src_sap = (struct sockaddr *)&sin; + break; + case AF_INET6: + ipv6_addr_copy(&sin6.sin6_addr, &rqstp->rq_daddr.addr6); + ni.src_sap = (struct sockaddr *)&sin6; + break; + default: + return NULL; + } - ssin.sin_addr = rqstp->rq_daddr.addr; - return nlm_lookup_host(1, svc_addr_in(rqstp), - rqstp->rq_prot, rqstp->rq_vers, - hostname, hostname_len, &ssin); + return nlm_lookup_host(&ni); } /* @@ -207,9 +371,8 @@ nlm_bind_host(struct nlm_host *host) { struct rpc_clnt *clnt; - dprintk("lockd: nlm_bind_host("NIPQUAD_FMT"->"NIPQUAD_FMT")\n", - NIPQUAD(host->h_saddr.sin_addr), - NIPQUAD(host->h_addr.sin_addr)); + dprintk("lockd: nlm_bind_host %s (%s), my addr=%s\n", + host->h_name, host->h_addrbuf, host->h_srcaddrbuf); /* Lock host handle */ mutex_lock(&host->h_mutex); @@ -221,7 +384,7 @@ nlm_bind_host(struct nlm_host *host) if (time_after_eq(jiffies, host->h_nextrebind)) { rpc_force_rebind(clnt); host->h_nextrebind = jiffies + NLM_HOST_REBIND; - dprintk("lockd: next rebind in %ld jiffies\n", + dprintk("lockd: next rebind in %lu jiffies\n", host->h_nextrebind - jiffies); } } else { @@ -234,9 +397,9 @@ nlm_bind_host(struct nlm_host *host) }; struct rpc_create_args args = { .protocol = host->h_proto, - .address = (struct sockaddr *)&host->h_addr, - .addrsize = sizeof(host->h_addr), - .saddress = (struct sockaddr *)&host->h_saddr, + .address = nlm_addr(host), + .addrsize = host->h_addrlen, + .saddress = nlm_srcaddr(host), .timeout = &timeparms, .servername = host->h_name, .program = &nlm_program, @@ -324,12 +487,16 @@ void nlm_host_rebooted(const struct sockaddr_in *sin, struct nsm_handle *nsm; struct nlm_host *host; - dprintk("lockd: nlm_host_rebooted(%s, %u.%u.%u.%u)\n", - hostname, NIPQUAD(sin->sin_addr)); - - /* Find the NSM handle for this peer */ - if (!(nsm = __nsm_find(sin, hostname, hostname_len, 0))) + nsm = nsm_find((struct sockaddr *)sin, sizeof(*sin), + hostname, hostname_len, 0); + if (nsm == NULL) { + dprintk("lockd: never saw rebooted peer '%.*s' before\n", + hostname_len, hostname); return; + } + + dprintk("lockd: nlm_host_rebooted(%.*s, %s)\n", + hostname_len, hostname, nsm->sm_addrbuf); /* When reclaiming locks on this peer, make sure that * we set up a new notification */ @@ -461,22 +628,23 @@ nlm_gc_hosts(void) static LIST_HEAD(nsm_handles); static DEFINE_SPINLOCK(nsm_lock); -static struct nsm_handle * -__nsm_find(const struct sockaddr_in *sin, - const char *hostname, unsigned int hostname_len, - int create) +static struct nsm_handle *nsm_find(const struct sockaddr *sap, + const size_t salen, + const char *hostname, + const size_t hostname_len, + const int create) { struct nsm_handle *nsm = NULL; struct nsm_handle *pos; - if (!sin) + if (!sap) return NULL; if (hostname && memchr(hostname, '/', hostname_len) != NULL) { if (printk_ratelimit()) { printk(KERN_WARNING "Invalid hostname \"%.*s\" " "in NFS lock request\n", - hostname_len, hostname); + (int)hostname_len, hostname); } return NULL; } @@ -489,7 +657,7 @@ retry: if (strlen(pos->sm_name) != hostname_len || memcmp(pos->sm_name, hostname, hostname_len)) continue; - } else if (!nlm_cmp_addr(&pos->sm_addr, sin)) + } else if (!nlm_cmp_addr(nsm_addr(pos), sap)) continue; atomic_inc(&pos->sm_count); kfree(nsm); @@ -509,10 +677,13 @@ retry: if (nsm == NULL) return NULL; - nsm->sm_addr = *sin; + memcpy(nsm_addr(nsm), sap, salen); + nsm->sm_addrlen = salen; nsm->sm_name = (char *) (nsm + 1); memcpy(nsm->sm_name, hostname, hostname_len); nsm->sm_name[hostname_len] = '\0'; + nlm_display_address((struct sockaddr *)&nsm->sm_addr, + nsm->sm_addrbuf, sizeof(nsm->sm_addrbuf)); atomic_set(&nsm->sm_count, 1); goto retry; @@ -521,13 +692,6 @@ found: return nsm; } -static struct nsm_handle * -nsm_find(const struct sockaddr_in *sin, const char *hostname, - unsigned int hostname_len) -{ - return __nsm_find(sin, hostname, hostname_len, 1); -} - /* * Release an NSM handle */ diff --git a/fs/lockd/mon.c b/fs/lockd/mon.c index e4d563543b11..4e7e958e8f67 100644 --- a/fs/lockd/mon.c +++ b/fs/lockd/mon.c @@ -51,7 +51,7 @@ nsm_mon_unmon(struct nsm_handle *nsm, u32 proc, struct nsm_res *res) memset(&args, 0, sizeof(args)); args.mon_name = nsm->sm_name; - args.addr = nsm->sm_addr.sin_addr.s_addr; + args.addr = nsm_addr_in(nsm)->sin_addr.s_addr; args.prog = NLM_PROGRAM; args.vers = 3; args.proc = NLMPROC_NSM_NOTIFY; diff --git a/fs/lockd/svc.c b/fs/lockd/svc.c index 5bd9bf0fa9df..c631a83931ce 100644 --- a/fs/lockd/svc.c +++ b/fs/lockd/svc.c @@ -51,7 +51,6 @@ static DEFINE_MUTEX(nlmsvc_mutex); static unsigned int nlmsvc_users; static struct task_struct *nlmsvc_task; static struct svc_rqst *nlmsvc_rqst; -int nlmsvc_grace_period; unsigned long nlmsvc_timeout; /* @@ -85,27 +84,23 @@ static unsigned long get_lockd_grace_period(void) return nlm_timeout * 5 * HZ; } -unsigned long get_nfs_grace_period(void) -{ - unsigned long lockdgrace = get_lockd_grace_period(); - unsigned long nfsdgrace = 0; - - if (nlmsvc_ops) - nfsdgrace = nlmsvc_ops->get_grace_period(); - - return max(lockdgrace, nfsdgrace); -} -EXPORT_SYMBOL(get_nfs_grace_period); +static struct lock_manager lockd_manager = { +}; -static unsigned long set_grace_period(void) +static void grace_ender(struct work_struct *not_used) { - nlmsvc_grace_period = 1; - return get_nfs_grace_period() + jiffies; + locks_end_grace(&lockd_manager); } -static inline void clear_grace_period(void) +static DECLARE_DELAYED_WORK(grace_period_end, grace_ender); + +static void set_grace_period(void) { - nlmsvc_grace_period = 0; + unsigned long grace_period = get_lockd_grace_period(); + + locks_start_grace(&lockd_manager); + cancel_delayed_work_sync(&grace_period_end); + schedule_delayed_work(&grace_period_end, grace_period); } /* @@ -116,7 +111,6 @@ lockd(void *vrqstp) { int err = 0, preverr = 0; struct svc_rqst *rqstp = vrqstp; - unsigned long grace_period_expire; /* try_to_freeze() is called from svc_recv() */ set_freezable(); @@ -139,7 +133,7 @@ lockd(void *vrqstp) nlm_timeout = LOCKD_DFLT_TIMEO; nlmsvc_timeout = nlm_timeout * HZ; - grace_period_expire = set_grace_period(); + set_grace_period(); /* * The main request loop. We don't terminate until the last @@ -153,21 +147,12 @@ lockd(void *vrqstp) flush_signals(current); if (nlmsvc_ops) { nlmsvc_invalidate_all(); - grace_period_expire = set_grace_period(); + set_grace_period(); } continue; } - /* - * Retry any blocked locks that have been notified by - * the VFS. Don't do this during grace period. - * (Theoretically, there shouldn't even be blocked locks - * during grace period). - */ - if (!nlmsvc_grace_period) { - timeout = nlmsvc_retry_blocked(); - } else if (time_before(grace_period_expire, jiffies)) - clear_grace_period(); + timeout = nlmsvc_retry_blocked(); /* * Find a socket with data available and call its @@ -195,6 +180,7 @@ lockd(void *vrqstp) svc_process(rqstp); } flush_signals(current); + cancel_delayed_work_sync(&grace_period_end); if (nlmsvc_ops) nlmsvc_invalidate_all(); nlm_shutdown_hosts(); @@ -203,25 +189,28 @@ lockd(void *vrqstp) } /* - * Make any sockets that are needed but not present. - * If nlm_udpport or nlm_tcpport were set as module - * options, make those sockets unconditionally + * Ensure there are active UDP and TCP listeners for lockd. + * + * Even if we have only TCP NFS mounts and/or TCP NFSDs, some + * local services (such as rpc.statd) still require UDP, and + * some NFS servers do not yet support NLM over TCP. + * + * Returns zero if all listeners are available; otherwise a + * negative errno value is returned. */ -static int make_socks(struct svc_serv *serv, int proto) +static int make_socks(struct svc_serv *serv) { static int warned; struct svc_xprt *xprt; int err = 0; - if (proto == IPPROTO_UDP || nlm_udpport) { - xprt = svc_find_xprt(serv, "udp", 0, 0); - if (!xprt) - err = svc_create_xprt(serv, "udp", nlm_udpport, - SVC_SOCK_DEFAULTS); - else - svc_xprt_put(xprt); - } - if (err >= 0 && (proto == IPPROTO_TCP || nlm_tcpport)) { + xprt = svc_find_xprt(serv, "udp", 0, 0); + if (!xprt) + err = svc_create_xprt(serv, "udp", nlm_udpport, + SVC_SOCK_DEFAULTS); + else + svc_xprt_put(xprt); + if (err >= 0) { xprt = svc_find_xprt(serv, "tcp", 0, 0); if (!xprt) err = svc_create_xprt(serv, "tcp", nlm_tcpport, @@ -241,8 +230,7 @@ static int make_socks(struct svc_serv *serv, int proto) /* * Bring up the lockd process if it's not already up. */ -int -lockd_up(int proto) /* Maybe add a 'family' option when IPv6 is supported ?? */ +int lockd_up(void) { struct svc_serv *serv; int error = 0; @@ -251,11 +239,8 @@ lockd_up(int proto) /* Maybe add a 'family' option when IPv6 is supported ?? */ /* * Check whether we're already up and running. */ - if (nlmsvc_rqst) { - if (proto) - error = make_socks(nlmsvc_rqst->rq_server, proto); + if (nlmsvc_rqst) goto out; - } /* * Sanity check: if there's no pid, @@ -266,13 +251,14 @@ lockd_up(int proto) /* Maybe add a 'family' option when IPv6 is supported ?? */ "lockd_up: no pid, %d users??\n", nlmsvc_users); error = -ENOMEM; - serv = svc_create(&nlmsvc_program, LOCKD_BUFSIZE, NULL); + serv = svc_create(&nlmsvc_program, LOCKD_BUFSIZE, AF_INET, NULL); if (!serv) { printk(KERN_WARNING "lockd_up: create service failed\n"); goto out; } - if ((error = make_socks(serv, proto)) < 0) + error = make_socks(serv); + if (error < 0) goto destroy_and_out; /* diff --git a/fs/lockd/svc4proc.c b/fs/lockd/svc4proc.c index 4a714f64515b..014f6ce48172 100644 --- a/fs/lockd/svc4proc.c +++ b/fs/lockd/svc4proc.c @@ -88,12 +88,6 @@ nlm4svc_proc_test(struct svc_rqst *rqstp, struct nlm_args *argp, dprintk("lockd: TEST4 called\n"); resp->cookie = argp->cookie; - /* Don't accept test requests during grace period */ - if (nlmsvc_grace_period) { - resp->status = nlm_lck_denied_grace_period; - return rc; - } - /* Obtain client and file */ if ((resp->status = nlm4svc_retrieve_args(rqstp, argp, &host, &file))) return resp->status == nlm_drop_reply ? rpc_drop_reply :rpc_success; @@ -122,12 +116,6 @@ nlm4svc_proc_lock(struct svc_rqst *rqstp, struct nlm_args *argp, resp->cookie = argp->cookie; - /* Don't accept new lock requests during grace period */ - if (nlmsvc_grace_period && !argp->reclaim) { - resp->status = nlm_lck_denied_grace_period; - return rc; - } - /* Obtain client and file */ if ((resp->status = nlm4svc_retrieve_args(rqstp, argp, &host, &file))) return resp->status == nlm_drop_reply ? rpc_drop_reply :rpc_success; @@ -146,7 +134,8 @@ nlm4svc_proc_lock(struct svc_rqst *rqstp, struct nlm_args *argp, /* Now try to lock the file */ resp->status = nlmsvc_lock(rqstp, file, host, &argp->lock, - argp->block, &argp->cookie); + argp->block, &argp->cookie, + argp->reclaim); if (resp->status == nlm_drop_reply) rc = rpc_drop_reply; else @@ -169,7 +158,7 @@ nlm4svc_proc_cancel(struct svc_rqst *rqstp, struct nlm_args *argp, resp->cookie = argp->cookie; /* Don't accept requests during grace period */ - if (nlmsvc_grace_period) { + if (locks_in_grace()) { resp->status = nlm_lck_denied_grace_period; return rpc_success; } @@ -202,7 +191,7 @@ nlm4svc_proc_unlock(struct svc_rqst *rqstp, struct nlm_args *argp, resp->cookie = argp->cookie; /* Don't accept new lock requests during grace period */ - if (nlmsvc_grace_period) { + if (locks_in_grace()) { resp->status = nlm_lck_denied_grace_period; return rpc_success; } @@ -231,7 +220,7 @@ nlm4svc_proc_granted(struct svc_rqst *rqstp, struct nlm_args *argp, resp->cookie = argp->cookie; dprintk("lockd: GRANTED called\n"); - resp->status = nlmclnt_grant(svc_addr_in(rqstp), &argp->lock); + resp->status = nlmclnt_grant(svc_addr(rqstp), &argp->lock); dprintk("lockd: GRANTED status %d\n", ntohl(resp->status)); return rpc_success; } @@ -341,7 +330,7 @@ nlm4svc_proc_share(struct svc_rqst *rqstp, struct nlm_args *argp, resp->cookie = argp->cookie; /* Don't accept new lock requests during grace period */ - if (nlmsvc_grace_period && !argp->reclaim) { + if (locks_in_grace() && !argp->reclaim) { resp->status = nlm_lck_denied_grace_period; return rpc_success; } @@ -374,7 +363,7 @@ nlm4svc_proc_unshare(struct svc_rqst *rqstp, struct nlm_args *argp, resp->cookie = argp->cookie; /* Don't accept requests during grace period */ - if (nlmsvc_grace_period) { + if (locks_in_grace()) { resp->status = nlm_lck_denied_grace_period; return rpc_success; } @@ -432,11 +421,9 @@ nlm4svc_proc_sm_notify(struct svc_rqst *rqstp, struct nlm_reboot *argp, { struct sockaddr_in saddr; - memcpy(&saddr, svc_addr_in(rqstp), sizeof(saddr)); - dprintk("lockd: SM_NOTIFY called\n"); - if (saddr.sin_addr.s_addr != htonl(INADDR_LOOPBACK) - || ntohs(saddr.sin_port) >= 1024) { + + if (!nlm_privileged_requester(rqstp)) { char buf[RPC_MAX_ADDRBUFLEN]; printk(KERN_WARNING "lockd: rejected NSM callback from %s\n", svc_print_addr(rqstp, buf, sizeof(buf))); diff --git a/fs/lockd/svclock.c b/fs/lockd/svclock.c index cf0d5c2c318d..6063a8e4b9f3 100644 --- a/fs/lockd/svclock.c +++ b/fs/lockd/svclock.c @@ -360,7 +360,7 @@ nlmsvc_defer_lock_rqst(struct svc_rqst *rqstp, struct nlm_block *block) __be32 nlmsvc_lock(struct svc_rqst *rqstp, struct nlm_file *file, struct nlm_host *host, struct nlm_lock *lock, int wait, - struct nlm_cookie *cookie) + struct nlm_cookie *cookie, int reclaim) { struct nlm_block *block = NULL; int error; @@ -406,6 +406,15 @@ nlmsvc_lock(struct svc_rqst *rqstp, struct nlm_file *file, goto out; } + if (locks_in_grace() && !reclaim) { + ret = nlm_lck_denied_grace_period; + goto out; + } + if (reclaim && !locks_in_grace()) { + ret = nlm_lck_denied_grace_period; + goto out; + } + if (!wait) lock->fl.fl_flags &= ~FL_SLEEP; error = vfs_lock_file(file->f_file, F_SETLK, &lock->fl, NULL); @@ -502,6 +511,10 @@ nlmsvc_testlock(struct svc_rqst *rqstp, struct nlm_file *file, goto out; } + if (locks_in_grace()) { + ret = nlm_lck_denied_grace_period; + goto out; + } error = vfs_test_lock(file->f_file, &lock->fl); if (error == FILE_LOCK_DEFERRED) { ret = nlmsvc_defer_lock_rqst(rqstp, block); @@ -582,6 +595,9 @@ nlmsvc_cancel_blocked(struct nlm_file *file, struct nlm_lock *lock) (long long)lock->fl.fl_start, (long long)lock->fl.fl_end); + if (locks_in_grace()) + return nlm_lck_denied_grace_period; + mutex_lock(&file->f_mutex); block = nlmsvc_lookup_block(file, lock); mutex_unlock(&file->f_mutex); diff --git a/fs/lockd/svcproc.c b/fs/lockd/svcproc.c index 76262c1986f2..548b0bb2b84d 100644 --- a/fs/lockd/svcproc.c +++ b/fs/lockd/svcproc.c @@ -117,12 +117,6 @@ nlmsvc_proc_test(struct svc_rqst *rqstp, struct nlm_args *argp, dprintk("lockd: TEST called\n"); resp->cookie = argp->cookie; - /* Don't accept test requests during grace period */ - if (nlmsvc_grace_period) { - resp->status = nlm_lck_denied_grace_period; - return rc; - } - /* Obtain client and file */ if ((resp->status = nlmsvc_retrieve_args(rqstp, argp, &host, &file))) return resp->status == nlm_drop_reply ? rpc_drop_reply :rpc_success; @@ -152,12 +146,6 @@ nlmsvc_proc_lock(struct svc_rqst *rqstp, struct nlm_args *argp, resp->cookie = argp->cookie; - /* Don't accept new lock requests during grace period */ - if (nlmsvc_grace_period && !argp->reclaim) { - resp->status = nlm_lck_denied_grace_period; - return rc; - } - /* Obtain client and file */ if ((resp->status = nlmsvc_retrieve_args(rqstp, argp, &host, &file))) return resp->status == nlm_drop_reply ? rpc_drop_reply :rpc_success; @@ -176,7 +164,8 @@ nlmsvc_proc_lock(struct svc_rqst *rqstp, struct nlm_args *argp, /* Now try to lock the file */ resp->status = cast_status(nlmsvc_lock(rqstp, file, host, &argp->lock, - argp->block, &argp->cookie)); + argp->block, &argp->cookie, + argp->reclaim)); if (resp->status == nlm_drop_reply) rc = rpc_drop_reply; else @@ -199,7 +188,7 @@ nlmsvc_proc_cancel(struct svc_rqst *rqstp, struct nlm_args *argp, resp->cookie = argp->cookie; /* Don't accept requests during grace period */ - if (nlmsvc_grace_period) { + if (locks_in_grace()) { resp->status = nlm_lck_denied_grace_period; return rpc_success; } @@ -232,7 +221,7 @@ nlmsvc_proc_unlock(struct svc_rqst *rqstp, struct nlm_args *argp, resp->cookie = argp->cookie; /* Don't accept new lock requests during grace period */ - if (nlmsvc_grace_period) { + if (locks_in_grace()) { resp->status = nlm_lck_denied_grace_period; return rpc_success; } @@ -261,7 +250,7 @@ nlmsvc_proc_granted(struct svc_rqst *rqstp, struct nlm_args *argp, resp->cookie = argp->cookie; dprintk("lockd: GRANTED called\n"); - resp->status = nlmclnt_grant(svc_addr_in(rqstp), &argp->lock); + resp->status = nlmclnt_grant(svc_addr(rqstp), &argp->lock); dprintk("lockd: GRANTED status %d\n", ntohl(resp->status)); return rpc_success; } @@ -373,7 +362,7 @@ nlmsvc_proc_share(struct svc_rqst *rqstp, struct nlm_args *argp, resp->cookie = argp->cookie; /* Don't accept new lock requests during grace period */ - if (nlmsvc_grace_period && !argp->reclaim) { + if (locks_in_grace() && !argp->reclaim) { resp->status = nlm_lck_denied_grace_period; return rpc_success; } @@ -406,7 +395,7 @@ nlmsvc_proc_unshare(struct svc_rqst *rqstp, struct nlm_args *argp, resp->cookie = argp->cookie; /* Don't accept requests during grace period */ - if (nlmsvc_grace_period) { + if (locks_in_grace()) { resp->status = nlm_lck_denied_grace_period; return rpc_success; } @@ -464,11 +453,9 @@ nlmsvc_proc_sm_notify(struct svc_rqst *rqstp, struct nlm_reboot *argp, { struct sockaddr_in saddr; - memcpy(&saddr, svc_addr_in(rqstp), sizeof(saddr)); - dprintk("lockd: SM_NOTIFY called\n"); - if (saddr.sin_addr.s_addr != htonl(INADDR_LOOPBACK) - || ntohs(saddr.sin_port) >= 1024) { + + if (!nlm_privileged_requester(rqstp)) { char buf[RPC_MAX_ADDRBUFLEN]; printk(KERN_WARNING "lockd: rejected NSM callback from %s\n", svc_print_addr(rqstp, buf, sizeof(buf))); diff --git a/fs/lockd/svcsubs.c b/fs/lockd/svcsubs.c index 198b4e55b373..34c2766e27c7 100644 --- a/fs/lockd/svcsubs.c +++ b/fs/lockd/svcsubs.c @@ -418,7 +418,7 @@ EXPORT_SYMBOL_GPL(nlmsvc_unlock_all_by_sb); static int nlmsvc_match_ip(void *datap, struct nlm_host *host) { - return nlm_cmp_addr(&host->h_saddr, datap); + return nlm_cmp_addr(nlm_srcaddr(host), datap); } /** diff --git a/fs/lockd/xdr.c b/fs/lockd/xdr.c index 3e459e18cc31..1f226290c67c 100644 --- a/fs/lockd/xdr.c +++ b/fs/lockd/xdr.c @@ -351,8 +351,6 @@ nlmsvc_decode_reboot(struct svc_rqst *rqstp, __be32 *p, struct nlm_reboot *argp) argp->state = ntohl(*p++); /* Preserve the address in network byte order */ argp->addr = *p++; - argp->vers = *p++; - argp->proto = *p++; return xdr_argsize_check(rqstp, p); } diff --git a/fs/lockd/xdr4.c b/fs/lockd/xdr4.c index 43ff9397e6c6..50c493a8ad8e 100644 --- a/fs/lockd/xdr4.c +++ b/fs/lockd/xdr4.c @@ -358,8 +358,6 @@ nlm4svc_decode_reboot(struct svc_rqst *rqstp, __be32 *p, struct nlm_reboot *argp argp->state = ntohl(*p++); /* Preserve the address in network byte order */ argp->addr = *p++; - argp->vers = *p++; - argp->proto = *p++; return xdr_argsize_check(rqstp, p); } diff --git a/fs/nfs/callback.c b/fs/nfs/callback.c index f447f4b4476c..6a09760c5960 100644 --- a/fs/nfs/callback.c +++ b/fs/nfs/callback.c @@ -105,7 +105,8 @@ int nfs_callback_up(void) mutex_lock(&nfs_callback_mutex); if (nfs_callback_info.users++ || nfs_callback_info.task != NULL) goto out; - serv = svc_create(&nfs4_callback_program, NFS4_CALLBACK_BUFSIZE, NULL); + serv = svc_create(&nfs4_callback_program, NFS4_CALLBACK_BUFSIZE, + AF_INET, NULL); ret = -ENOMEM; if (!serv) goto out_err; diff --git a/fs/nfsd/lockd.c b/fs/nfsd/lockd.c index 15c6faeec77c..b2786a5f9afe 100644 --- a/fs/nfsd/lockd.c +++ b/fs/nfsd/lockd.c @@ -70,7 +70,6 @@ nlm_fclose(struct file *filp) static struct nlmsvc_binding nfsd_nlm_ops = { .fopen = nlm_fopen, /* open file for locking */ .fclose = nlm_fclose, /* close file */ - .get_grace_period = get_nfs4_grace_period, }; void diff --git a/fs/nfsd/nfs3proc.c b/fs/nfsd/nfs3proc.c index 4d617ea28cfc..9dbd2eb91281 100644 --- a/fs/nfsd/nfs3proc.c +++ b/fs/nfsd/nfs3proc.c @@ -63,7 +63,8 @@ nfsd3_proc_getattr(struct svc_rqst *rqstp, struct nfsd_fhandle *argp, SVCFH_fmt(&argp->fh)); fh_copy(&resp->fh, &argp->fh); - nfserr = fh_verify(rqstp, &resp->fh, 0, NFSD_MAY_NOP); + nfserr = fh_verify(rqstp, &resp->fh, 0, + NFSD_MAY_NOP | NFSD_MAY_BYPASS_GSS_ON_ROOT); if (nfserr) RETURN_STATUS(nfserr); @@ -530,7 +531,7 @@ nfsd3_proc_fsstat(struct svc_rqst * rqstp, struct nfsd_fhandle *argp, dprintk("nfsd: FSSTAT(3) %s\n", SVCFH_fmt(&argp->fh)); - nfserr = nfsd_statfs(rqstp, &argp->fh, &resp->stats); + nfserr = nfsd_statfs(rqstp, &argp->fh, &resp->stats, 0); fh_put(&argp->fh); RETURN_STATUS(nfserr); } @@ -558,7 +559,8 @@ nfsd3_proc_fsinfo(struct svc_rqst * rqstp, struct nfsd_fhandle *argp, resp->f_maxfilesize = ~(u32) 0; resp->f_properties = NFS3_FSF_DEFAULT; - nfserr = fh_verify(rqstp, &argp->fh, 0, NFSD_MAY_NOP); + nfserr = fh_verify(rqstp, &argp->fh, 0, + NFSD_MAY_NOP | NFSD_MAY_BYPASS_GSS_ON_ROOT); /* Check special features of the file system. May request * different read/write sizes for file systems known to have diff --git a/fs/nfsd/nfs4callback.c b/fs/nfsd/nfs4callback.c index 702fa577aa6e..094747a1227c 100644 --- a/fs/nfsd/nfs4callback.c +++ b/fs/nfsd/nfs4callback.c @@ -225,7 +225,8 @@ encode_cb_recall(struct xdr_stream *xdr, struct nfs4_cb_recall *cb_rec) RESERVE_SPACE(12+sizeof(cb_rec->cbr_stateid) + len); WRITE32(OP_CB_RECALL); - WRITEMEM(&cb_rec->cbr_stateid, sizeof(stateid_t)); + WRITE32(cb_rec->cbr_stateid.si_generation); + WRITEMEM(&cb_rec->cbr_stateid.si_opaque, sizeof(stateid_opaque_t)); WRITE32(cb_rec->cbr_trunc); WRITE32(len); WRITEMEM(cb_rec->cbr_fhval, len); @@ -379,6 +380,7 @@ static int do_probe_callback(void *data) .addrsize = sizeof(addr), .timeout = &timeparms, .program = &cb_program, + .prognumber = cb->cb_prog, .version = nfs_cb_version[1]->number, .authflavor = RPC_AUTH_UNIX, /* XXX: need AUTH_GSS... */ .flags = (RPC_CLNT_CREATE_NOPING | RPC_CLNT_CREATE_QUIET), @@ -396,9 +398,6 @@ static int do_probe_callback(void *data) addr.sin_port = htons(cb->cb_port); addr.sin_addr.s_addr = htonl(cb->cb_addr); - /* Initialize rpc_stat */ - memset(args.program->stats, 0, sizeof(struct rpc_stat)); - /* Create RPC client */ client = rpc_create(&args); if (IS_ERR(client)) { diff --git a/fs/nfsd/nfs4proc.c b/fs/nfsd/nfs4proc.c index e5b51ffafc6c..669461e291ae 100644 --- a/fs/nfsd/nfs4proc.c +++ b/fs/nfsd/nfs4proc.c @@ -201,10 +201,10 @@ nfsd4_open(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate, /* Openowner is now set, so sequence id will get bumped. Now we need * these checks before we do any creates: */ status = nfserr_grace; - if (nfs4_in_grace() && open->op_claim_type != NFS4_OPEN_CLAIM_PREVIOUS) + if (locks_in_grace() && open->op_claim_type != NFS4_OPEN_CLAIM_PREVIOUS) goto out; status = nfserr_no_grace; - if (!nfs4_in_grace() && open->op_claim_type == NFS4_OPEN_CLAIM_PREVIOUS) + if (!locks_in_grace() && open->op_claim_type == NFS4_OPEN_CLAIM_PREVIOUS) goto out; switch (open->op_claim_type) { @@ -575,7 +575,7 @@ nfsd4_remove(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate, { __be32 status; - if (nfs4_in_grace()) + if (locks_in_grace()) return nfserr_grace; status = nfsd_unlink(rqstp, &cstate->current_fh, 0, remove->rm_name, remove->rm_namelen); @@ -596,7 +596,7 @@ nfsd4_rename(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate, if (!cstate->save_fh.fh_dentry) return status; - if (nfs4_in_grace() && !(cstate->save_fh.fh_export->ex_flags + if (locks_in_grace() && !(cstate->save_fh.fh_export->ex_flags & NFSEXP_NOSUBTREECHECK)) return nfserr_grace; status = nfsd_rename(rqstp, &cstate->save_fh, rename->rn_sname, diff --git a/fs/nfsd/nfs4state.c b/fs/nfsd/nfs4state.c index 1578d7a2667e..0cc7ff5d5ab5 100644 --- a/fs/nfsd/nfs4state.c +++ b/fs/nfsd/nfs4state.c @@ -61,7 +61,6 @@ static time_t lease_time = 90; /* default lease time */ static time_t user_lease_time = 90; static time_t boot_time; -static int in_grace = 1; static u32 current_ownerid = 1; static u32 current_fileid = 1; static u32 current_delegid = 1; @@ -1640,7 +1639,7 @@ nfs4_open_delegation(struct svc_fh *fh, struct nfsd4_open *open, struct nfs4_sta case NFS4_OPEN_CLAIM_NULL: /* Let's not give out any delegations till everyone's * had the chance to reclaim theirs.... */ - if (nfs4_in_grace()) + if (locks_in_grace()) goto out; if (!atomic_read(&cb->cb_set) || !sop->so_confirmed) goto out; @@ -1816,12 +1815,15 @@ out: return status; } +struct lock_manager nfsd4_manager = { +}; + static void -end_grace(void) +nfsd4_end_grace(void) { dprintk("NFSD: end of grace period\n"); nfsd4_recdir_purge_old(); - in_grace = 0; + locks_end_grace(&nfsd4_manager); } static time_t @@ -1838,8 +1840,8 @@ nfs4_laundromat(void) nfs4_lock_state(); dprintk("NFSD: laundromat service - starting\n"); - if (in_grace) - end_grace(); + if (locks_in_grace()) + nfsd4_end_grace(); list_for_each_safe(pos, next, &client_lru) { clp = list_entry(pos, struct nfs4_client, cl_lru); if (time_after((unsigned long)clp->cl_time, (unsigned long)cutoff)) { @@ -1974,7 +1976,7 @@ check_special_stateids(svc_fh *current_fh, stateid_t *stateid, int flags) return nfserr_bad_stateid; else if (ONE_STATEID(stateid) && (flags & RD_STATE)) return nfs_ok; - else if (nfs4_in_grace()) { + else if (locks_in_grace()) { /* Answer in remaining cases depends on existance of * conflicting state; so we must wait out the grace period. */ return nfserr_grace; @@ -1993,7 +1995,7 @@ check_special_stateids(svc_fh *current_fh, stateid_t *stateid, int flags) static inline int io_during_grace_disallowed(struct inode *inode, int flags) { - return nfs4_in_grace() && (flags & (RD_STATE | WR_STATE)) + return locks_in_grace() && (flags & (RD_STATE | WR_STATE)) && mandatory_lock(inode); } @@ -2693,10 +2695,10 @@ nfsd4_lock(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate, filp = lock_stp->st_vfs_file; status = nfserr_grace; - if (nfs4_in_grace() && !lock->lk_reclaim) + if (locks_in_grace() && !lock->lk_reclaim) goto out; status = nfserr_no_grace; - if (!nfs4_in_grace() && lock->lk_reclaim) + if (!locks_in_grace() && lock->lk_reclaim) goto out; locks_init_lock(&file_lock); @@ -2779,7 +2781,7 @@ nfsd4_lockt(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate, int error; __be32 status; - if (nfs4_in_grace()) + if (locks_in_grace()) return nfserr_grace; if (check_lock_length(lockt->lt_offset, lockt->lt_length)) @@ -3192,9 +3194,9 @@ __nfs4_state_start(void) unsigned long grace_time; boot_time = get_seconds(); - grace_time = get_nfs_grace_period(); + grace_time = get_nfs4_grace_period(); lease_time = user_lease_time; - in_grace = 1; + locks_start_grace(&nfsd4_manager); printk(KERN_INFO "NFSD: starting %ld-second grace period\n", grace_time/HZ); laundry_wq = create_singlethread_workqueue("nfsd4"); @@ -3213,12 +3215,6 @@ nfs4_state_start(void) return; } -int -nfs4_in_grace(void) -{ - return in_grace; -} - time_t nfs4_lease_time(void) { diff --git a/fs/nfsd/nfs4xdr.c b/fs/nfsd/nfs4xdr.c index 14ba4d9b2859..afcdf4b76843 100644 --- a/fs/nfsd/nfs4xdr.c +++ b/fs/nfsd/nfs4xdr.c @@ -413,6 +413,18 @@ out_nfserr: } static __be32 +nfsd4_decode_stateid(struct nfsd4_compoundargs *argp, stateid_t *sid) +{ + DECODE_HEAD; + + READ_BUF(sizeof(stateid_t)); + READ32(sid->si_generation); + COPYMEM(&sid->si_opaque, sizeof(stateid_opaque_t)); + + DECODE_TAIL; +} + +static __be32 nfsd4_decode_access(struct nfsd4_compoundargs *argp, struct nfsd4_access *access) { DECODE_HEAD; @@ -429,10 +441,9 @@ nfsd4_decode_close(struct nfsd4_compoundargs *argp, struct nfsd4_close *close) DECODE_HEAD; close->cl_stateowner = NULL; - READ_BUF(4 + sizeof(stateid_t)); + READ_BUF(4); READ32(close->cl_seqid); - READ32(close->cl_stateid.si_generation); - COPYMEM(&close->cl_stateid.si_opaque, sizeof(stateid_opaque_t)); + return nfsd4_decode_stateid(argp, &close->cl_stateid); DECODE_TAIL; } @@ -493,13 +504,7 @@ nfsd4_decode_create(struct nfsd4_compoundargs *argp, struct nfsd4_create *create static inline __be32 nfsd4_decode_delegreturn(struct nfsd4_compoundargs *argp, struct nfsd4_delegreturn *dr) { - DECODE_HEAD; - - READ_BUF(sizeof(stateid_t)); - READ32(dr->dr_stateid.si_generation); - COPYMEM(&dr->dr_stateid.si_opaque, sizeof(stateid_opaque_t)); - - DECODE_TAIL; + return nfsd4_decode_stateid(argp, &dr->dr_stateid); } static inline __be32 @@ -542,20 +547,22 @@ nfsd4_decode_lock(struct nfsd4_compoundargs *argp, struct nfsd4_lock *lock) READ32(lock->lk_is_new); if (lock->lk_is_new) { - READ_BUF(36); + READ_BUF(4); READ32(lock->lk_new_open_seqid); - READ32(lock->lk_new_open_stateid.si_generation); - - COPYMEM(&lock->lk_new_open_stateid.si_opaque, sizeof(stateid_opaque_t)); + status = nfsd4_decode_stateid(argp, &lock->lk_new_open_stateid); + if (status) + return status; + READ_BUF(8 + sizeof(clientid_t)); READ32(lock->lk_new_lock_seqid); COPYMEM(&lock->lk_new_clientid, sizeof(clientid_t)); READ32(lock->lk_new_owner.len); READ_BUF(lock->lk_new_owner.len); READMEM(lock->lk_new_owner.data, lock->lk_new_owner.len); } else { - READ_BUF(20); - READ32(lock->lk_old_lock_stateid.si_generation); - COPYMEM(&lock->lk_old_lock_stateid.si_opaque, sizeof(stateid_opaque_t)); + status = nfsd4_decode_stateid(argp, &lock->lk_old_lock_stateid); + if (status) + return status; + READ_BUF(4); READ32(lock->lk_old_lock_seqid); } @@ -587,13 +594,15 @@ nfsd4_decode_locku(struct nfsd4_compoundargs *argp, struct nfsd4_locku *locku) DECODE_HEAD; locku->lu_stateowner = NULL; - READ_BUF(24 + sizeof(stateid_t)); + READ_BUF(8); READ32(locku->lu_type); if ((locku->lu_type < NFS4_READ_LT) || (locku->lu_type > NFS4_WRITEW_LT)) goto xdr_error; READ32(locku->lu_seqid); - READ32(locku->lu_stateid.si_generation); - COPYMEM(&locku->lu_stateid.si_opaque, sizeof(stateid_opaque_t)); + status = nfsd4_decode_stateid(argp, &locku->lu_stateid); + if (status) + return status; + READ_BUF(16); READ64(locku->lu_offset); READ64(locku->lu_length); @@ -678,8 +687,10 @@ nfsd4_decode_open(struct nfsd4_compoundargs *argp, struct nfsd4_open *open) READ32(open->op_delegate_type); break; case NFS4_OPEN_CLAIM_DELEGATE_CUR: - READ_BUF(sizeof(stateid_t) + 4); - COPYMEM(&open->op_delegate_stateid, sizeof(stateid_t)); + status = nfsd4_decode_stateid(argp, &open->op_delegate_stateid); + if (status) + return status; + READ_BUF(4); READ32(open->op_fname.len); READ_BUF(open->op_fname.len); SAVEMEM(open->op_fname.data, open->op_fname.len); @@ -699,9 +710,10 @@ nfsd4_decode_open_confirm(struct nfsd4_compoundargs *argp, struct nfsd4_open_con DECODE_HEAD; open_conf->oc_stateowner = NULL; - READ_BUF(4 + sizeof(stateid_t)); - READ32(open_conf->oc_req_stateid.si_generation); - COPYMEM(&open_conf->oc_req_stateid.si_opaque, sizeof(stateid_opaque_t)); + status = nfsd4_decode_stateid(argp, &open_conf->oc_req_stateid); + if (status) + return status; + READ_BUF(4); READ32(open_conf->oc_seqid); DECODE_TAIL; @@ -713,9 +725,10 @@ nfsd4_decode_open_downgrade(struct nfsd4_compoundargs *argp, struct nfsd4_open_d DECODE_HEAD; open_down->od_stateowner = NULL; - READ_BUF(12 + sizeof(stateid_t)); - READ32(open_down->od_stateid.si_generation); - COPYMEM(&open_down->od_stateid.si_opaque, sizeof(stateid_opaque_t)); + status = nfsd4_decode_stateid(argp, &open_down->od_stateid); + if (status) + return status; + READ_BUF(12); READ32(open_down->od_seqid); READ32(open_down->od_share_access); READ32(open_down->od_share_deny); @@ -743,9 +756,10 @@ nfsd4_decode_read(struct nfsd4_compoundargs *argp, struct nfsd4_read *read) { DECODE_HEAD; - READ_BUF(sizeof(stateid_t) + 12); - READ32(read->rd_stateid.si_generation); - COPYMEM(&read->rd_stateid.si_opaque, sizeof(stateid_opaque_t)); + status = nfsd4_decode_stateid(argp, &read->rd_stateid); + if (status) + return status; + READ_BUF(12); READ64(read->rd_offset); READ32(read->rd_length); @@ -834,15 +848,13 @@ nfsd4_decode_secinfo(struct nfsd4_compoundargs *argp, static __be32 nfsd4_decode_setattr(struct nfsd4_compoundargs *argp, struct nfsd4_setattr *setattr) { - DECODE_HEAD; - - READ_BUF(sizeof(stateid_t)); - READ32(setattr->sa_stateid.si_generation); - COPYMEM(&setattr->sa_stateid.si_opaque, sizeof(stateid_opaque_t)); - if ((status = nfsd4_decode_fattr(argp, setattr->sa_bmval, &setattr->sa_iattr, &setattr->sa_acl))) - goto out; + __be32 status; - DECODE_TAIL; + status = nfsd4_decode_stateid(argp, &setattr->sa_stateid); + if (status) + return status; + return nfsd4_decode_fattr(argp, setattr->sa_bmval, + &setattr->sa_iattr, &setattr->sa_acl); } static __be32 @@ -927,9 +939,10 @@ nfsd4_decode_write(struct nfsd4_compoundargs *argp, struct nfsd4_write *write) int len; DECODE_HEAD; - READ_BUF(sizeof(stateid_opaque_t) + 20); - READ32(write->wr_stateid.si_generation); - COPYMEM(&write->wr_stateid.si_opaque, sizeof(stateid_opaque_t)); + status = nfsd4_decode_stateid(argp, &write->wr_stateid); + if (status) + return status; + READ_BUF(16); READ64(write->wr_offset); READ32(write->wr_stable_how); if (write->wr_stable_how > 2) @@ -1183,7 +1196,6 @@ nfsd4_decode_compound(struct nfsd4_compoundargs *argp) * Header routine to setup seqid operation replay cache */ #define ENCODE_SEQID_OP_HEAD \ - __be32 *p; \ __be32 *save; \ \ save = resp->p; @@ -1950,6 +1962,17 @@ fail: return -EINVAL; } +static void +nfsd4_encode_stateid(struct nfsd4_compoundres *resp, stateid_t *sid) +{ + ENCODE_HEAD; + + RESERVE_SPACE(sizeof(stateid_t)); + WRITE32(sid->si_generation); + WRITEMEM(&sid->si_opaque, sizeof(stateid_opaque_t)); + ADJUST_ARGS(); +} + static __be32 nfsd4_encode_access(struct nfsd4_compoundres *resp, __be32 nfserr, struct nfsd4_access *access) { @@ -1969,12 +1992,9 @@ nfsd4_encode_close(struct nfsd4_compoundres *resp, __be32 nfserr, struct nfsd4_c { ENCODE_SEQID_OP_HEAD; - if (!nfserr) { - RESERVE_SPACE(sizeof(stateid_t)); - WRITE32(close->cl_stateid.si_generation); - WRITEMEM(&close->cl_stateid.si_opaque, sizeof(stateid_opaque_t)); - ADJUST_ARGS(); - } + if (!nfserr) + nfsd4_encode_stateid(resp, &close->cl_stateid); + ENCODE_SEQID_OP_TAIL(close->cl_stateowner); return nfserr; } @@ -2074,12 +2094,9 @@ nfsd4_encode_lock(struct nfsd4_compoundres *resp, __be32 nfserr, struct nfsd4_lo { ENCODE_SEQID_OP_HEAD; - if (!nfserr) { - RESERVE_SPACE(4 + sizeof(stateid_t)); - WRITE32(lock->lk_resp_stateid.si_generation); - WRITEMEM(&lock->lk_resp_stateid.si_opaque, sizeof(stateid_opaque_t)); - ADJUST_ARGS(); - } else if (nfserr == nfserr_denied) + if (!nfserr) + nfsd4_encode_stateid(resp, &lock->lk_resp_stateid); + else if (nfserr == nfserr_denied) nfsd4_encode_lock_denied(resp, &lock->lk_denied); ENCODE_SEQID_OP_TAIL(lock->lk_replay_owner); @@ -2099,13 +2116,9 @@ nfsd4_encode_locku(struct nfsd4_compoundres *resp, __be32 nfserr, struct nfsd4_l { ENCODE_SEQID_OP_HEAD; - if (!nfserr) { - RESERVE_SPACE(sizeof(stateid_t)); - WRITE32(locku->lu_stateid.si_generation); - WRITEMEM(&locku->lu_stateid.si_opaque, sizeof(stateid_opaque_t)); - ADJUST_ARGS(); - } - + if (!nfserr) + nfsd4_encode_stateid(resp, &locku->lu_stateid); + ENCODE_SEQID_OP_TAIL(locku->lu_stateowner); return nfserr; } @@ -2128,14 +2141,14 @@ nfsd4_encode_link(struct nfsd4_compoundres *resp, __be32 nfserr, struct nfsd4_li static __be32 nfsd4_encode_open(struct nfsd4_compoundres *resp, __be32 nfserr, struct nfsd4_open *open) { + ENCODE_HEAD; ENCODE_SEQID_OP_HEAD; if (nfserr) goto out; - RESERVE_SPACE(36 + sizeof(stateid_t)); - WRITE32(open->op_stateid.si_generation); - WRITEMEM(&open->op_stateid.si_opaque, sizeof(stateid_opaque_t)); + nfsd4_encode_stateid(resp, &open->op_stateid); + RESERVE_SPACE(40); WRITECINFO(open->op_cinfo); WRITE32(open->op_rflags); WRITE32(2); @@ -2148,8 +2161,8 @@ nfsd4_encode_open(struct nfsd4_compoundres *resp, __be32 nfserr, struct nfsd4_op case NFS4_OPEN_DELEGATE_NONE: break; case NFS4_OPEN_DELEGATE_READ: - RESERVE_SPACE(20 + sizeof(stateid_t)); - WRITEMEM(&open->op_delegate_stateid, sizeof(stateid_t)); + nfsd4_encode_stateid(resp, &open->op_delegate_stateid); + RESERVE_SPACE(20); WRITE32(open->op_recall); /* @@ -2162,8 +2175,8 @@ nfsd4_encode_open(struct nfsd4_compoundres *resp, __be32 nfserr, struct nfsd4_op ADJUST_ARGS(); break; case NFS4_OPEN_DELEGATE_WRITE: - RESERVE_SPACE(32 + sizeof(stateid_t)); - WRITEMEM(&open->op_delegate_stateid, sizeof(stateid_t)); + nfsd4_encode_stateid(resp, &open->op_delegate_stateid); + RESERVE_SPACE(32); WRITE32(0); /* @@ -2195,13 +2208,9 @@ static __be32 nfsd4_encode_open_confirm(struct nfsd4_compoundres *resp, __be32 nfserr, struct nfsd4_open_confirm *oc) { ENCODE_SEQID_OP_HEAD; - - if (!nfserr) { - RESERVE_SPACE(sizeof(stateid_t)); - WRITE32(oc->oc_resp_stateid.si_generation); - WRITEMEM(&oc->oc_resp_stateid.si_opaque, sizeof(stateid_opaque_t)); - ADJUST_ARGS(); - } + + if (!nfserr) + nfsd4_encode_stateid(resp, &oc->oc_resp_stateid); ENCODE_SEQID_OP_TAIL(oc->oc_stateowner); return nfserr; @@ -2211,13 +2220,9 @@ static __be32 nfsd4_encode_open_downgrade(struct nfsd4_compoundres *resp, __be32 nfserr, struct nfsd4_open_downgrade *od) { ENCODE_SEQID_OP_HEAD; - - if (!nfserr) { - RESERVE_SPACE(sizeof(stateid_t)); - WRITE32(od->od_stateid.si_generation); - WRITEMEM(&od->od_stateid.si_opaque, sizeof(stateid_opaque_t)); - ADJUST_ARGS(); - } + + if (!nfserr) + nfsd4_encode_stateid(resp, &od->od_stateid); ENCODE_SEQID_OP_TAIL(od->od_stateowner); return nfserr; diff --git a/fs/nfsd/nfsctl.c b/fs/nfsd/nfsctl.c index c53e65f8f3a2..97543df58242 100644 --- a/fs/nfsd/nfsctl.c +++ b/fs/nfsd/nfsctl.c @@ -614,10 +614,9 @@ static ssize_t __write_ports(struct file *file, char *buf, size_t size) return -EINVAL; err = nfsd_create_serv(); if (!err) { - int proto = 0; - err = svc_addsock(nfsd_serv, fd, buf, &proto); + err = svc_addsock(nfsd_serv, fd, buf); if (err >= 0) { - err = lockd_up(proto); + err = lockd_up(); if (err < 0) svc_sock_names(buf+strlen(buf)+1, nfsd_serv, buf); } diff --git a/fs/nfsd/nfsfh.c b/fs/nfsd/nfsfh.c index ea37c96f0445..cd25d91895a1 100644 --- a/fs/nfsd/nfsfh.c +++ b/fs/nfsd/nfsfh.c @@ -302,17 +302,27 @@ fh_verify(struct svc_rqst *rqstp, struct svc_fh *fhp, int type, int access) if (error) goto out; - if (!(access & NFSD_MAY_LOCK)) { - /* - * pseudoflavor restrictions are not enforced on NLM, - * which clients virtually always use auth_sys for, - * even while using RPCSEC_GSS for NFS. - */ - error = check_nfsd_access(exp, rqstp); - if (error) - goto out; - } + /* + * pseudoflavor restrictions are not enforced on NLM, + * which clients virtually always use auth_sys for, + * even while using RPCSEC_GSS for NFS. + */ + if (access & NFSD_MAY_LOCK) + goto skip_pseudoflavor_check; + /* + * Clients may expect to be able to use auth_sys during mount, + * even if they use gss for everything else; see section 2.3.2 + * of rfc 2623. + */ + if (access & NFSD_MAY_BYPASS_GSS_ON_ROOT + && exp->ex_path.dentry == dentry) + goto skip_pseudoflavor_check; + + error = check_nfsd_access(exp, rqstp); + if (error) + goto out; +skip_pseudoflavor_check: /* Finally, check access permissions. */ error = nfsd_permission(rqstp, exp, dentry, access); diff --git a/fs/nfsd/nfsproc.c b/fs/nfsd/nfsproc.c index 0766f95d236a..5cffeca7acef 100644 --- a/fs/nfsd/nfsproc.c +++ b/fs/nfsd/nfsproc.c @@ -65,7 +65,8 @@ nfsd_proc_getattr(struct svc_rqst *rqstp, struct nfsd_fhandle *argp, dprintk("nfsd: GETATTR %s\n", SVCFH_fmt(&argp->fh)); fh_copy(&resp->fh, &argp->fh); - nfserr = fh_verify(rqstp, &resp->fh, 0, NFSD_MAY_NOP); + nfserr = fh_verify(rqstp, &resp->fh, 0, + NFSD_MAY_NOP | NFSD_MAY_BYPASS_GSS_ON_ROOT); return nfsd_return_attrs(nfserr, resp); } @@ -521,7 +522,8 @@ nfsd_proc_statfs(struct svc_rqst * rqstp, struct nfsd_fhandle *argp, dprintk("nfsd: STATFS %s\n", SVCFH_fmt(&argp->fh)); - nfserr = nfsd_statfs(rqstp, &argp->fh, &resp->stats); + nfserr = nfsd_statfs(rqstp, &argp->fh, &resp->stats, + NFSD_MAY_BYPASS_GSS_ON_ROOT); fh_put(&argp->fh); return nfserr; } diff --git a/fs/nfsd/nfssvc.c b/fs/nfsd/nfssvc.c index 80292ff5e924..59eeb46f82c5 100644 --- a/fs/nfsd/nfssvc.c +++ b/fs/nfsd/nfssvc.c @@ -229,6 +229,7 @@ int nfsd_create_serv(void) atomic_set(&nfsd_busy, 0); nfsd_serv = svc_create_pooled(&nfsd_program, nfsd_max_blksize, + AF_INET, nfsd_last_thread, nfsd, THIS_MODULE); if (nfsd_serv == NULL) err = -ENOMEM; @@ -243,25 +244,20 @@ static int nfsd_init_socks(int port) if (!list_empty(&nfsd_serv->sv_permsocks)) return 0; - error = lockd_up(IPPROTO_UDP); - if (error >= 0) { - error = svc_create_xprt(nfsd_serv, "udp", port, + error = svc_create_xprt(nfsd_serv, "udp", port, SVC_SOCK_DEFAULTS); - if (error < 0) - lockd_down(); - } if (error < 0) return error; - error = lockd_up(IPPROTO_TCP); - if (error >= 0) { - error = svc_create_xprt(nfsd_serv, "tcp", port, + error = svc_create_xprt(nfsd_serv, "tcp", port, SVC_SOCK_DEFAULTS); - if (error < 0) - lockd_down(); - } if (error < 0) return error; + + error = lockd_up(); + if (error < 0) + return error; + return 0; } diff --git a/fs/nfsd/vfs.c b/fs/nfsd/vfs.c index 18060bed5267..aa1d0d6489a1 100644 --- a/fs/nfsd/vfs.c +++ b/fs/nfsd/vfs.c @@ -83,7 +83,6 @@ struct raparm_hbucket { spinlock_t pb_lock; } ____cacheline_aligned_in_smp; -static struct raparms * raparml; #define RAPARM_HASH_BITS 4 #define RAPARM_HASH_SIZE (1<<RAPARM_HASH_BITS) #define RAPARM_HASH_MASK (RAPARM_HASH_SIZE-1) @@ -1866,9 +1865,9 @@ out: * N.B. After this call fhp needs an fh_put */ __be32 -nfsd_statfs(struct svc_rqst *rqstp, struct svc_fh *fhp, struct kstatfs *stat) +nfsd_statfs(struct svc_rqst *rqstp, struct svc_fh *fhp, struct kstatfs *stat, int access) { - __be32 err = fh_verify(rqstp, fhp, 0, NFSD_MAY_NOP); + __be32 err = fh_verify(rqstp, fhp, 0, NFSD_MAY_NOP | access); if (!err && vfs_statfs(fhp->fh_dentry,stat)) err = nfserr_io; return err; @@ -1966,11 +1965,20 @@ nfsd_permission(struct svc_rqst *rqstp, struct svc_export *exp, void nfsd_racache_shutdown(void) { - if (!raparml) - return; + struct raparms *raparm, *last_raparm; + unsigned int i; + dprintk("nfsd: freeing readahead buffers.\n"); - kfree(raparml); - raparml = NULL; + + for (i = 0; i < RAPARM_HASH_SIZE; i++) { + raparm = raparm_hash[i].pb_head; + while(raparm) { + last_raparm = raparm; + raparm = raparm->p_next; + kfree(last_raparm); + } + raparm_hash[i].pb_head = NULL; + } } /* * Initialize readahead param cache @@ -1981,35 +1989,38 @@ nfsd_racache_init(int cache_size) int i; int j = 0; int nperbucket; + struct raparms **raparm = NULL; - if (raparml) + if (raparm_hash[0].pb_head) return 0; - if (cache_size < 2*RAPARM_HASH_SIZE) - cache_size = 2*RAPARM_HASH_SIZE; - raparml = kcalloc(cache_size, sizeof(struct raparms), GFP_KERNEL); - - if (!raparml) { - printk(KERN_WARNING - "nfsd: Could not allocate memory read-ahead cache.\n"); - return -ENOMEM; - } + nperbucket = DIV_ROUND_UP(cache_size, RAPARM_HASH_SIZE); + if (nperbucket < 2) + nperbucket = 2; + cache_size = nperbucket * RAPARM_HASH_SIZE; dprintk("nfsd: allocating %d readahead buffers.\n", cache_size); - for (i = 0 ; i < RAPARM_HASH_SIZE ; i++) { - raparm_hash[i].pb_head = NULL; + + for (i = 0; i < RAPARM_HASH_SIZE; i++) { spin_lock_init(&raparm_hash[i].pb_lock); - } - nperbucket = DIV_ROUND_UP(cache_size, RAPARM_HASH_SIZE); - for (i = 0; i < cache_size - 1; i++) { - if (i % nperbucket == 0) - raparm_hash[j++].pb_head = raparml + i; - if (i % nperbucket < nperbucket-1) - raparml[i].p_next = raparml + i + 1; + + raparm = &raparm_hash[i].pb_head; + for (j = 0; j < nperbucket; j++) { + *raparm = kzalloc(sizeof(struct raparms), GFP_KERNEL); + if (!*raparm) + goto out_nomem; + raparm = &(*raparm)->p_next; + } + *raparm = NULL; } nfsdstats.ra_size = cache_size; return 0; + +out_nomem: + dprintk("nfsd: kmalloc failed, freeing readahead buffers\n"); + nfsd_racache_shutdown(); + return -ENOMEM; } #if defined(CONFIG_NFSD_V2_ACL) || defined(CONFIG_NFSD_V3_ACL) diff --git a/fs/ocfs2/Makefile b/fs/ocfs2/Makefile index f6956de56fdb..589dcdfdfe3c 100644 --- a/fs/ocfs2/Makefile +++ b/fs/ocfs2/Makefile @@ -34,7 +34,8 @@ ocfs2-objs := \ symlink.o \ sysfile.o \ uptodate.o \ - ver.o + ver.o \ + xattr.o ocfs2_stackglue-objs := stackglue.o ocfs2_stack_o2cb-objs := stack_o2cb.o diff --git a/fs/ocfs2/alloc.c b/fs/ocfs2/alloc.c index 29ff57ec5d1f..0cc2deb9394c 100644 --- a/fs/ocfs2/alloc.c +++ b/fs/ocfs2/alloc.c @@ -49,6 +49,340 @@ #include "buffer_head_io.h" + +/* + * Operations for a specific extent tree type. + * + * To implement an on-disk btree (extent tree) type in ocfs2, add + * an ocfs2_extent_tree_operations structure and the matching + * ocfs2_init_<thingy>_extent_tree() function. That's pretty much it + * for the allocation portion of the extent tree. + */ +struct ocfs2_extent_tree_operations { + /* + * last_eb_blk is the block number of the right most leaf extent + * block. Most on-disk structures containing an extent tree store + * this value for fast access. The ->eo_set_last_eb_blk() and + * ->eo_get_last_eb_blk() operations access this value. They are + * both required. + */ + void (*eo_set_last_eb_blk)(struct ocfs2_extent_tree *et, + u64 blkno); + u64 (*eo_get_last_eb_blk)(struct ocfs2_extent_tree *et); + + /* + * The on-disk structure usually keeps track of how many total + * clusters are stored in this extent tree. This function updates + * that value. new_clusters is the delta, and must be + * added to the total. Required. + */ + void (*eo_update_clusters)(struct inode *inode, + struct ocfs2_extent_tree *et, + u32 new_clusters); + + /* + * If ->eo_insert_check() exists, it is called before rec is + * inserted into the extent tree. It is optional. + */ + int (*eo_insert_check)(struct inode *inode, + struct ocfs2_extent_tree *et, + struct ocfs2_extent_rec *rec); + int (*eo_sanity_check)(struct inode *inode, struct ocfs2_extent_tree *et); + + /* + * -------------------------------------------------------------- + * The remaining are internal to ocfs2_extent_tree and don't have + * accessor functions + */ + + /* + * ->eo_fill_root_el() takes et->et_object and sets et->et_root_el. + * It is required. + */ + void (*eo_fill_root_el)(struct ocfs2_extent_tree *et); + + /* + * ->eo_fill_max_leaf_clusters sets et->et_max_leaf_clusters if + * it exists. If it does not, et->et_max_leaf_clusters is set + * to 0 (unlimited). Optional. + */ + void (*eo_fill_max_leaf_clusters)(struct inode *inode, + struct ocfs2_extent_tree *et); +}; + + +/* + * Pre-declare ocfs2_dinode_et_ops so we can use it as a sanity check + * in the methods. + */ +static u64 ocfs2_dinode_get_last_eb_blk(struct ocfs2_extent_tree *et); +static void ocfs2_dinode_set_last_eb_blk(struct ocfs2_extent_tree *et, + u64 blkno); +static void ocfs2_dinode_update_clusters(struct inode *inode, + struct ocfs2_extent_tree *et, + u32 clusters); +static int ocfs2_dinode_insert_check(struct inode *inode, + struct ocfs2_extent_tree *et, + struct ocfs2_extent_rec *rec); +static int ocfs2_dinode_sanity_check(struct inode *inode, + struct ocfs2_extent_tree *et); +static void ocfs2_dinode_fill_root_el(struct ocfs2_extent_tree *et); +static struct ocfs2_extent_tree_operations ocfs2_dinode_et_ops = { + .eo_set_last_eb_blk = ocfs2_dinode_set_last_eb_blk, + .eo_get_last_eb_blk = ocfs2_dinode_get_last_eb_blk, + .eo_update_clusters = ocfs2_dinode_update_clusters, + .eo_insert_check = ocfs2_dinode_insert_check, + .eo_sanity_check = ocfs2_dinode_sanity_check, + .eo_fill_root_el = ocfs2_dinode_fill_root_el, +}; + +static void ocfs2_dinode_set_last_eb_blk(struct ocfs2_extent_tree *et, + u64 blkno) +{ + struct ocfs2_dinode *di = et->et_object; + + BUG_ON(et->et_ops != &ocfs2_dinode_et_ops); + di->i_last_eb_blk = cpu_to_le64(blkno); +} + +static u64 ocfs2_dinode_get_last_eb_blk(struct ocfs2_extent_tree *et) +{ + struct ocfs2_dinode *di = et->et_object; + + BUG_ON(et->et_ops != &ocfs2_dinode_et_ops); + return le64_to_cpu(di->i_last_eb_blk); +} + +static void ocfs2_dinode_update_clusters(struct inode *inode, + struct ocfs2_extent_tree *et, + u32 clusters) +{ + struct ocfs2_dinode *di = et->et_object; + + le32_add_cpu(&di->i_clusters, clusters); + spin_lock(&OCFS2_I(inode)->ip_lock); + OCFS2_I(inode)->ip_clusters = le32_to_cpu(di->i_clusters); + spin_unlock(&OCFS2_I(inode)->ip_lock); +} + +static int ocfs2_dinode_insert_check(struct inode *inode, + struct ocfs2_extent_tree *et, + struct ocfs2_extent_rec *rec) +{ + struct ocfs2_super *osb = OCFS2_SB(inode->i_sb); + + BUG_ON(OCFS2_I(inode)->ip_dyn_features & OCFS2_INLINE_DATA_FL); + mlog_bug_on_msg(!ocfs2_sparse_alloc(osb) && + (OCFS2_I(inode)->ip_clusters != rec->e_cpos), + "Device %s, asking for sparse allocation: inode %llu, " + "cpos %u, clusters %u\n", + osb->dev_str, + (unsigned long long)OCFS2_I(inode)->ip_blkno, + rec->e_cpos, + OCFS2_I(inode)->ip_clusters); + + return 0; +} + +static int ocfs2_dinode_sanity_check(struct inode *inode, + struct ocfs2_extent_tree *et) +{ + int ret = 0; + struct ocfs2_dinode *di; + + BUG_ON(et->et_ops != &ocfs2_dinode_et_ops); + + di = et->et_object; + if (!OCFS2_IS_VALID_DINODE(di)) { + ret = -EIO; + ocfs2_error(inode->i_sb, + "Inode %llu has invalid path root", + (unsigned long long)OCFS2_I(inode)->ip_blkno); + } + + return ret; +} + +static void ocfs2_dinode_fill_root_el(struct ocfs2_extent_tree *et) +{ + struct ocfs2_dinode *di = et->et_object; + + et->et_root_el = &di->id2.i_list; +} + + +static void ocfs2_xattr_value_fill_root_el(struct ocfs2_extent_tree *et) +{ + struct ocfs2_xattr_value_root *xv = et->et_object; + + et->et_root_el = &xv->xr_list; +} + +static void ocfs2_xattr_value_set_last_eb_blk(struct ocfs2_extent_tree *et, + u64 blkno) +{ + struct ocfs2_xattr_value_root *xv = + (struct ocfs2_xattr_value_root *)et->et_object; + + xv->xr_last_eb_blk = cpu_to_le64(blkno); +} + +static u64 ocfs2_xattr_value_get_last_eb_blk(struct ocfs2_extent_tree *et) +{ + struct ocfs2_xattr_value_root *xv = + (struct ocfs2_xattr_value_root *) et->et_object; + + return le64_to_cpu(xv->xr_last_eb_blk); +} + +static void ocfs2_xattr_value_update_clusters(struct inode *inode, + struct ocfs2_extent_tree *et, + u32 clusters) +{ + struct ocfs2_xattr_value_root *xv = + (struct ocfs2_xattr_value_root *)et->et_object; + + le32_add_cpu(&xv->xr_clusters, clusters); +} + +static struct ocfs2_extent_tree_operations ocfs2_xattr_value_et_ops = { + .eo_set_last_eb_blk = ocfs2_xattr_value_set_last_eb_blk, + .eo_get_last_eb_blk = ocfs2_xattr_value_get_last_eb_blk, + .eo_update_clusters = ocfs2_xattr_value_update_clusters, + .eo_fill_root_el = ocfs2_xattr_value_fill_root_el, +}; + +static void ocfs2_xattr_tree_fill_root_el(struct ocfs2_extent_tree *et) +{ + struct ocfs2_xattr_block *xb = et->et_object; + + et->et_root_el = &xb->xb_attrs.xb_root.xt_list; +} + +static void ocfs2_xattr_tree_fill_max_leaf_clusters(struct inode *inode, + struct ocfs2_extent_tree *et) +{ + et->et_max_leaf_clusters = + ocfs2_clusters_for_bytes(inode->i_sb, + OCFS2_MAX_XATTR_TREE_LEAF_SIZE); +} + +static void ocfs2_xattr_tree_set_last_eb_blk(struct ocfs2_extent_tree *et, + u64 blkno) +{ + struct ocfs2_xattr_block *xb = et->et_object; + struct ocfs2_xattr_tree_root *xt = &xb->xb_attrs.xb_root; + + xt->xt_last_eb_blk = cpu_to_le64(blkno); +} + +static u64 ocfs2_xattr_tree_get_last_eb_blk(struct ocfs2_extent_tree *et) +{ + struct ocfs2_xattr_block *xb = et->et_object; + struct ocfs2_xattr_tree_root *xt = &xb->xb_attrs.xb_root; + + return le64_to_cpu(xt->xt_last_eb_blk); +} + +static void ocfs2_xattr_tree_update_clusters(struct inode *inode, + struct ocfs2_extent_tree *et, + u32 clusters) +{ + struct ocfs2_xattr_block *xb = et->et_object; + + le32_add_cpu(&xb->xb_attrs.xb_root.xt_clusters, clusters); +} + +static struct ocfs2_extent_tree_operations ocfs2_xattr_tree_et_ops = { + .eo_set_last_eb_blk = ocfs2_xattr_tree_set_last_eb_blk, + .eo_get_last_eb_blk = ocfs2_xattr_tree_get_last_eb_blk, + .eo_update_clusters = ocfs2_xattr_tree_update_clusters, + .eo_fill_root_el = ocfs2_xattr_tree_fill_root_el, + .eo_fill_max_leaf_clusters = ocfs2_xattr_tree_fill_max_leaf_clusters, +}; + +static void __ocfs2_init_extent_tree(struct ocfs2_extent_tree *et, + struct inode *inode, + struct buffer_head *bh, + void *obj, + struct ocfs2_extent_tree_operations *ops) +{ + et->et_ops = ops; + et->et_root_bh = bh; + if (!obj) + obj = (void *)bh->b_data; + et->et_object = obj; + + et->et_ops->eo_fill_root_el(et); + if (!et->et_ops->eo_fill_max_leaf_clusters) + et->et_max_leaf_clusters = 0; + else + et->et_ops->eo_fill_max_leaf_clusters(inode, et); +} + +void ocfs2_init_dinode_extent_tree(struct ocfs2_extent_tree *et, + struct inode *inode, + struct buffer_head *bh) +{ + __ocfs2_init_extent_tree(et, inode, bh, NULL, &ocfs2_dinode_et_ops); +} + +void ocfs2_init_xattr_tree_extent_tree(struct ocfs2_extent_tree *et, + struct inode *inode, + struct buffer_head *bh) +{ + __ocfs2_init_extent_tree(et, inode, bh, NULL, + &ocfs2_xattr_tree_et_ops); +} + +void ocfs2_init_xattr_value_extent_tree(struct ocfs2_extent_tree *et, + struct inode *inode, + struct buffer_head *bh, + struct ocfs2_xattr_value_root *xv) +{ + __ocfs2_init_extent_tree(et, inode, bh, xv, + &ocfs2_xattr_value_et_ops); +} + +static inline void ocfs2_et_set_last_eb_blk(struct ocfs2_extent_tree *et, + u64 new_last_eb_blk) +{ + et->et_ops->eo_set_last_eb_blk(et, new_last_eb_blk); +} + +static inline u64 ocfs2_et_get_last_eb_blk(struct ocfs2_extent_tree *et) +{ + return et->et_ops->eo_get_last_eb_blk(et); +} + +static inline void ocfs2_et_update_clusters(struct inode *inode, + struct ocfs2_extent_tree *et, + u32 clusters) +{ + et->et_ops->eo_update_clusters(inode, et, clusters); +} + +static inline int ocfs2_et_insert_check(struct inode *inode, + struct ocfs2_extent_tree *et, + struct ocfs2_extent_rec *rec) +{ + int ret = 0; + + if (et->et_ops->eo_insert_check) + ret = et->et_ops->eo_insert_check(inode, et, rec); + return ret; +} + +static inline int ocfs2_et_sanity_check(struct inode *inode, + struct ocfs2_extent_tree *et) +{ + int ret = 0; + + if (et->et_ops->eo_sanity_check) + ret = et->et_ops->eo_sanity_check(inode, et); + return ret; +} + static void ocfs2_free_truncate_context(struct ocfs2_truncate_context *tc); static int ocfs2_cache_extent_block_free(struct ocfs2_cached_dealloc_ctxt *ctxt, struct ocfs2_extent_block *eb); @@ -205,17 +539,6 @@ static struct ocfs2_path *ocfs2_new_path(struct buffer_head *root_bh, } /* - * Allocate and initialize a new path based on a disk inode tree. - */ -static struct ocfs2_path *ocfs2_new_inode_path(struct buffer_head *di_bh) -{ - struct ocfs2_dinode *di = (struct ocfs2_dinode *)di_bh->b_data; - struct ocfs2_extent_list *el = &di->id2.i_list; - - return ocfs2_new_path(di_bh, el); -} - -/* * Convenience function to journal all components in a path. */ static int ocfs2_journal_access_path(struct inode *inode, handle_t *handle, @@ -368,39 +691,35 @@ struct ocfs2_merge_ctxt { */ int ocfs2_num_free_extents(struct ocfs2_super *osb, struct inode *inode, - struct ocfs2_dinode *fe) + struct ocfs2_extent_tree *et) { int retval; - struct ocfs2_extent_list *el; + struct ocfs2_extent_list *el = NULL; struct ocfs2_extent_block *eb; struct buffer_head *eb_bh = NULL; + u64 last_eb_blk = 0; mlog_entry_void(); - if (!OCFS2_IS_VALID_DINODE(fe)) { - OCFS2_RO_ON_INVALID_DINODE(inode->i_sb, fe); - retval = -EIO; - goto bail; - } + el = et->et_root_el; + last_eb_blk = ocfs2_et_get_last_eb_blk(et); - if (fe->i_last_eb_blk) { - retval = ocfs2_read_block(osb, le64_to_cpu(fe->i_last_eb_blk), - &eb_bh, OCFS2_BH_CACHED, inode); + if (last_eb_blk) { + retval = ocfs2_read_block(inode, last_eb_blk, + &eb_bh); if (retval < 0) { mlog_errno(retval); goto bail; } eb = (struct ocfs2_extent_block *) eb_bh->b_data; el = &eb->h_list; - } else - el = &fe->id2.i_list; + } BUG_ON(el->l_tree_depth != 0); retval = le16_to_cpu(el->l_count) - le16_to_cpu(el->l_next_free_rec); bail: - if (eb_bh) - brelse(eb_bh); + brelse(eb_bh); mlog_exit(retval); return retval; @@ -486,8 +805,7 @@ static int ocfs2_create_new_meta_bhs(struct ocfs2_super *osb, bail: if (status < 0) { for(i = 0; i < wanted; i++) { - if (bhs[i]) - brelse(bhs[i]); + brelse(bhs[i]); bhs[i] = NULL; } } @@ -531,7 +849,7 @@ static inline u32 ocfs2_sum_rightmost_rec(struct ocfs2_extent_list *el) static int ocfs2_add_branch(struct ocfs2_super *osb, handle_t *handle, struct inode *inode, - struct buffer_head *fe_bh, + struct ocfs2_extent_tree *et, struct buffer_head *eb_bh, struct buffer_head **last_eb_bh, struct ocfs2_alloc_context *meta_ac) @@ -540,7 +858,6 @@ static int ocfs2_add_branch(struct ocfs2_super *osb, u64 next_blkno, new_last_eb_blk; struct buffer_head *bh; struct buffer_head **new_eb_bhs = NULL; - struct ocfs2_dinode *fe; struct ocfs2_extent_block *eb; struct ocfs2_extent_list *eb_el; struct ocfs2_extent_list *el; @@ -550,13 +867,11 @@ static int ocfs2_add_branch(struct ocfs2_super *osb, BUG_ON(!last_eb_bh || !*last_eb_bh); - fe = (struct ocfs2_dinode *) fe_bh->b_data; - if (eb_bh) { eb = (struct ocfs2_extent_block *) eb_bh->b_data; el = &eb->h_list; } else - el = &fe->id2.i_list; + el = et->et_root_el; /* we never add a branch to a leaf. */ BUG_ON(!el->l_tree_depth); @@ -646,7 +961,7 @@ static int ocfs2_add_branch(struct ocfs2_super *osb, mlog_errno(status); goto bail; } - status = ocfs2_journal_access(handle, inode, fe_bh, + status = ocfs2_journal_access(handle, inode, et->et_root_bh, OCFS2_JOURNAL_ACCESS_WRITE); if (status < 0) { mlog_errno(status); @@ -662,7 +977,7 @@ static int ocfs2_add_branch(struct ocfs2_super *osb, } /* Link the new branch into the rest of the tree (el will - * either be on the fe, or the extent block passed in. */ + * either be on the root_bh, or the extent block passed in. */ i = le16_to_cpu(el->l_next_free_rec); el->l_recs[i].e_blkno = cpu_to_le64(next_blkno); el->l_recs[i].e_cpos = cpu_to_le32(new_cpos); @@ -671,7 +986,7 @@ static int ocfs2_add_branch(struct ocfs2_super *osb, /* fe needs a new last extent block pointer, as does the * next_leaf on the previously last-extent-block. */ - fe->i_last_eb_blk = cpu_to_le64(new_last_eb_blk); + ocfs2_et_set_last_eb_blk(et, new_last_eb_blk); eb = (struct ocfs2_extent_block *) (*last_eb_bh)->b_data; eb->h_next_leaf_blk = cpu_to_le64(new_last_eb_blk); @@ -679,7 +994,7 @@ static int ocfs2_add_branch(struct ocfs2_super *osb, status = ocfs2_journal_dirty(handle, *last_eb_bh); if (status < 0) mlog_errno(status); - status = ocfs2_journal_dirty(handle, fe_bh); + status = ocfs2_journal_dirty(handle, et->et_root_bh); if (status < 0) mlog_errno(status); if (eb_bh) { @@ -700,8 +1015,7 @@ static int ocfs2_add_branch(struct ocfs2_super *osb, bail: if (new_eb_bhs) { for (i = 0; i < new_blocks; i++) - if (new_eb_bhs[i]) - brelse(new_eb_bhs[i]); + brelse(new_eb_bhs[i]); kfree(new_eb_bhs); } @@ -717,16 +1031,15 @@ bail: static int ocfs2_shift_tree_depth(struct ocfs2_super *osb, handle_t *handle, struct inode *inode, - struct buffer_head *fe_bh, + struct ocfs2_extent_tree *et, struct ocfs2_alloc_context *meta_ac, struct buffer_head **ret_new_eb_bh) { int status, i; u32 new_clusters; struct buffer_head *new_eb_bh = NULL; - struct ocfs2_dinode *fe; struct ocfs2_extent_block *eb; - struct ocfs2_extent_list *fe_el; + struct ocfs2_extent_list *root_el; struct ocfs2_extent_list *eb_el; mlog_entry_void(); @@ -746,8 +1059,7 @@ static int ocfs2_shift_tree_depth(struct ocfs2_super *osb, } eb_el = &eb->h_list; - fe = (struct ocfs2_dinode *) fe_bh->b_data; - fe_el = &fe->id2.i_list; + root_el = et->et_root_el; status = ocfs2_journal_access(handle, inode, new_eb_bh, OCFS2_JOURNAL_ACCESS_CREATE); @@ -756,11 +1068,11 @@ static int ocfs2_shift_tree_depth(struct ocfs2_super *osb, goto bail; } - /* copy the fe data into the new extent block */ - eb_el->l_tree_depth = fe_el->l_tree_depth; - eb_el->l_next_free_rec = fe_el->l_next_free_rec; - for(i = 0; i < le16_to_cpu(fe_el->l_next_free_rec); i++) - eb_el->l_recs[i] = fe_el->l_recs[i]; + /* copy the root extent list data into the new extent block */ + eb_el->l_tree_depth = root_el->l_tree_depth; + eb_el->l_next_free_rec = root_el->l_next_free_rec; + for (i = 0; i < le16_to_cpu(root_el->l_next_free_rec); i++) + eb_el->l_recs[i] = root_el->l_recs[i]; status = ocfs2_journal_dirty(handle, new_eb_bh); if (status < 0) { @@ -768,7 +1080,7 @@ static int ocfs2_shift_tree_depth(struct ocfs2_super *osb, goto bail; } - status = ocfs2_journal_access(handle, inode, fe_bh, + status = ocfs2_journal_access(handle, inode, et->et_root_bh, OCFS2_JOURNAL_ACCESS_WRITE); if (status < 0) { mlog_errno(status); @@ -777,21 +1089,21 @@ static int ocfs2_shift_tree_depth(struct ocfs2_super *osb, new_clusters = ocfs2_sum_rightmost_rec(eb_el); - /* update fe now */ - le16_add_cpu(&fe_el->l_tree_depth, 1); - fe_el->l_recs[0].e_cpos = 0; - fe_el->l_recs[0].e_blkno = eb->h_blkno; - fe_el->l_recs[0].e_int_clusters = cpu_to_le32(new_clusters); - for(i = 1; i < le16_to_cpu(fe_el->l_next_free_rec); i++) - memset(&fe_el->l_recs[i], 0, sizeof(struct ocfs2_extent_rec)); - fe_el->l_next_free_rec = cpu_to_le16(1); + /* update root_bh now */ + le16_add_cpu(&root_el->l_tree_depth, 1); + root_el->l_recs[0].e_cpos = 0; + root_el->l_recs[0].e_blkno = eb->h_blkno; + root_el->l_recs[0].e_int_clusters = cpu_to_le32(new_clusters); + for (i = 1; i < le16_to_cpu(root_el->l_next_free_rec); i++) + memset(&root_el->l_recs[i], 0, sizeof(struct ocfs2_extent_rec)); + root_el->l_next_free_rec = cpu_to_le16(1); /* If this is our 1st tree depth shift, then last_eb_blk * becomes the allocated extent block */ - if (fe_el->l_tree_depth == cpu_to_le16(1)) - fe->i_last_eb_blk = eb->h_blkno; + if (root_el->l_tree_depth == cpu_to_le16(1)) + ocfs2_et_set_last_eb_blk(et, le64_to_cpu(eb->h_blkno)); - status = ocfs2_journal_dirty(handle, fe_bh); + status = ocfs2_journal_dirty(handle, et->et_root_bh); if (status < 0) { mlog_errno(status); goto bail; @@ -801,8 +1113,7 @@ static int ocfs2_shift_tree_depth(struct ocfs2_super *osb, new_eb_bh = NULL; status = 0; bail: - if (new_eb_bh) - brelse(new_eb_bh); + brelse(new_eb_bh); mlog_exit(status); return status; @@ -817,22 +1128,21 @@ bail: * 1) a lowest extent block is found, then we pass it back in * *lowest_eb_bh and return '0' * - * 2) the search fails to find anything, but the dinode has room. We + * 2) the search fails to find anything, but the root_el has room. We * pass NULL back in *lowest_eb_bh, but still return '0' * - * 3) the search fails to find anything AND the dinode is full, in + * 3) the search fails to find anything AND the root_el is full, in * which case we return > 0 * * return status < 0 indicates an error. */ static int ocfs2_find_branch_target(struct ocfs2_super *osb, struct inode *inode, - struct buffer_head *fe_bh, + struct ocfs2_extent_tree *et, struct buffer_head **target_bh) { int status = 0, i; u64 blkno; - struct ocfs2_dinode *fe; struct ocfs2_extent_block *eb; struct ocfs2_extent_list *el; struct buffer_head *bh = NULL; @@ -842,8 +1152,7 @@ static int ocfs2_find_branch_target(struct ocfs2_super *osb, *target_bh = NULL; - fe = (struct ocfs2_dinode *) fe_bh->b_data; - el = &fe->id2.i_list; + el = et->et_root_el; while(le16_to_cpu(el->l_tree_depth) > 1) { if (le16_to_cpu(el->l_next_free_rec) == 0) { @@ -864,13 +1173,10 @@ static int ocfs2_find_branch_target(struct ocfs2_super *osb, goto bail; } - if (bh) { - brelse(bh); - bh = NULL; - } + brelse(bh); + bh = NULL; - status = ocfs2_read_block(osb, blkno, &bh, OCFS2_BH_CACHED, - inode); + status = ocfs2_read_block(inode, blkno, &bh); if (status < 0) { mlog_errno(status); goto bail; @@ -886,8 +1192,7 @@ static int ocfs2_find_branch_target(struct ocfs2_super *osb, if (le16_to_cpu(el->l_next_free_rec) < le16_to_cpu(el->l_count)) { - if (lowest_bh) - brelse(lowest_bh); + brelse(lowest_bh); lowest_bh = bh; get_bh(lowest_bh); } @@ -895,14 +1200,13 @@ static int ocfs2_find_branch_target(struct ocfs2_super *osb, /* If we didn't find one and the fe doesn't have any room, * then return '1' */ - if (!lowest_bh - && (fe->id2.i_list.l_next_free_rec == fe->id2.i_list.l_count)) + el = et->et_root_el; + if (!lowest_bh && (el->l_next_free_rec == el->l_count)) status = 1; *target_bh = lowest_bh; bail: - if (bh) - brelse(bh); + brelse(bh); mlog_exit(status); return status; @@ -919,19 +1223,19 @@ bail: * *last_eb_bh will be updated by ocfs2_add_branch(). */ static int ocfs2_grow_tree(struct inode *inode, handle_t *handle, - struct buffer_head *di_bh, int *final_depth, + struct ocfs2_extent_tree *et, int *final_depth, struct buffer_head **last_eb_bh, struct ocfs2_alloc_context *meta_ac) { int ret, shift; - struct ocfs2_dinode *di = (struct ocfs2_dinode *)di_bh->b_data; - int depth = le16_to_cpu(di->id2.i_list.l_tree_depth); + struct ocfs2_extent_list *el = et->et_root_el; + int depth = le16_to_cpu(el->l_tree_depth); struct ocfs2_super *osb = OCFS2_SB(inode->i_sb); struct buffer_head *bh = NULL; BUG_ON(meta_ac == NULL); - shift = ocfs2_find_branch_target(osb, inode, di_bh, &bh); + shift = ocfs2_find_branch_target(osb, inode, et, &bh); if (shift < 0) { ret = shift; mlog_errno(ret); @@ -948,7 +1252,7 @@ static int ocfs2_grow_tree(struct inode *inode, handle_t *handle, /* ocfs2_shift_tree_depth will return us a buffer with * the new extent block (so we can pass that to * ocfs2_add_branch). */ - ret = ocfs2_shift_tree_depth(osb, handle, inode, di_bh, + ret = ocfs2_shift_tree_depth(osb, handle, inode, et, meta_ac, &bh); if (ret < 0) { mlog_errno(ret); @@ -975,7 +1279,7 @@ static int ocfs2_grow_tree(struct inode *inode, handle_t *handle, /* call ocfs2_add_branch to add the final part of the tree with * the new data. */ mlog(0, "add branch. bh = %p\n", bh); - ret = ocfs2_add_branch(osb, handle, inode, di_bh, bh, last_eb_bh, + ret = ocfs2_add_branch(osb, handle, inode, et, bh, last_eb_bh, meta_ac); if (ret < 0) { mlog_errno(ret); @@ -1236,8 +1540,7 @@ static int __ocfs2_find_path(struct inode *inode, brelse(bh); bh = NULL; - ret = ocfs2_read_block(OCFS2_SB(inode->i_sb), blkno, - &bh, OCFS2_BH_CACHED, inode); + ret = ocfs2_read_block(inode, blkno, &bh); if (ret) { mlog_errno(ret); goto out; @@ -2058,11 +2361,11 @@ static int ocfs2_rotate_subtree_left(struct inode *inode, handle_t *handle, struct ocfs2_path *right_path, int subtree_index, struct ocfs2_cached_dealloc_ctxt *dealloc, - int *deleted) + int *deleted, + struct ocfs2_extent_tree *et) { int ret, i, del_right_subtree = 0, right_has_empty = 0; - struct buffer_head *root_bh, *di_bh = path_root_bh(right_path); - struct ocfs2_dinode *di = (struct ocfs2_dinode *)di_bh->b_data; + struct buffer_head *root_bh, *et_root_bh = path_root_bh(right_path); struct ocfs2_extent_list *right_leaf_el, *left_leaf_el; struct ocfs2_extent_block *eb; @@ -2114,7 +2417,7 @@ static int ocfs2_rotate_subtree_left(struct inode *inode, handle_t *handle, * We have to update i_last_eb_blk during the meta * data delete. */ - ret = ocfs2_journal_access(handle, inode, di_bh, + ret = ocfs2_journal_access(handle, inode, et_root_bh, OCFS2_JOURNAL_ACCESS_WRITE); if (ret) { mlog_errno(ret); @@ -2189,7 +2492,7 @@ static int ocfs2_rotate_subtree_left(struct inode *inode, handle_t *handle, ocfs2_update_edge_lengths(inode, handle, left_path); eb = (struct ocfs2_extent_block *)path_leaf_bh(left_path)->b_data; - di->i_last_eb_blk = eb->h_blkno; + ocfs2_et_set_last_eb_blk(et, le64_to_cpu(eb->h_blkno)); /* * Removal of the extent in the left leaf was skipped @@ -2199,7 +2502,7 @@ static int ocfs2_rotate_subtree_left(struct inode *inode, handle_t *handle, if (right_has_empty) ocfs2_remove_empty_extent(left_leaf_el); - ret = ocfs2_journal_dirty(handle, di_bh); + ret = ocfs2_journal_dirty(handle, et_root_bh); if (ret) mlog_errno(ret); @@ -2322,7 +2625,8 @@ static int __ocfs2_rotate_tree_left(struct inode *inode, handle_t *handle, int orig_credits, struct ocfs2_path *path, struct ocfs2_cached_dealloc_ctxt *dealloc, - struct ocfs2_path **empty_extent_path) + struct ocfs2_path **empty_extent_path, + struct ocfs2_extent_tree *et) { int ret, subtree_root, deleted; u32 right_cpos; @@ -2395,7 +2699,7 @@ static int __ocfs2_rotate_tree_left(struct inode *inode, ret = ocfs2_rotate_subtree_left(inode, handle, left_path, right_path, subtree_root, - dealloc, &deleted); + dealloc, &deleted, et); if (ret == -EAGAIN) { /* * The rotation has to temporarily stop due to @@ -2438,29 +2742,20 @@ out: } static int ocfs2_remove_rightmost_path(struct inode *inode, handle_t *handle, - struct ocfs2_path *path, - struct ocfs2_cached_dealloc_ctxt *dealloc) + struct ocfs2_path *path, + struct ocfs2_cached_dealloc_ctxt *dealloc, + struct ocfs2_extent_tree *et) { int ret, subtree_index; u32 cpos; struct ocfs2_path *left_path = NULL; - struct ocfs2_dinode *di; struct ocfs2_extent_block *eb; struct ocfs2_extent_list *el; - /* - * XXX: This code assumes that the root is an inode, which is - * true for now but may change as tree code gets generic. - */ - di = (struct ocfs2_dinode *)path_root_bh(path)->b_data; - if (!OCFS2_IS_VALID_DINODE(di)) { - ret = -EIO; - ocfs2_error(inode->i_sb, - "Inode %llu has invalid path root", - (unsigned long long)OCFS2_I(inode)->ip_blkno); - goto out; - } + ret = ocfs2_et_sanity_check(inode, et); + if (ret) + goto out; /* * There's two ways we handle this depending on * whether path is the only existing one. @@ -2517,7 +2812,7 @@ static int ocfs2_remove_rightmost_path(struct inode *inode, handle_t *handle, ocfs2_update_edge_lengths(inode, handle, left_path); eb = (struct ocfs2_extent_block *)path_leaf_bh(left_path)->b_data; - di->i_last_eb_blk = eb->h_blkno; + ocfs2_et_set_last_eb_blk(et, le64_to_cpu(eb->h_blkno)); } else { /* * 'path' is also the leftmost path which @@ -2528,12 +2823,12 @@ static int ocfs2_remove_rightmost_path(struct inode *inode, handle_t *handle, */ ocfs2_unlink_path(inode, handle, dealloc, path, 1); - el = &di->id2.i_list; + el = et->et_root_el; el->l_tree_depth = 0; el->l_next_free_rec = 0; memset(&el->l_recs[0], 0, sizeof(struct ocfs2_extent_rec)); - di->i_last_eb_blk = 0; + ocfs2_et_set_last_eb_blk(et, 0); } ocfs2_journal_dirty(handle, path_root_bh(path)); @@ -2561,7 +2856,8 @@ out: */ static int ocfs2_rotate_tree_left(struct inode *inode, handle_t *handle, struct ocfs2_path *path, - struct ocfs2_cached_dealloc_ctxt *dealloc) + struct ocfs2_cached_dealloc_ctxt *dealloc, + struct ocfs2_extent_tree *et) { int ret, orig_credits = handle->h_buffer_credits; struct ocfs2_path *tmp_path = NULL, *restart_path = NULL; @@ -2575,7 +2871,7 @@ static int ocfs2_rotate_tree_left(struct inode *inode, handle_t *handle, if (path->p_tree_depth == 0) { rightmost_no_delete: /* - * In-inode extents. This is trivially handled, so do + * Inline extents. This is trivially handled, so do * it up front. */ ret = ocfs2_rotate_rightmost_leaf_left(inode, handle, @@ -2629,7 +2925,7 @@ rightmost_no_delete: */ ret = ocfs2_remove_rightmost_path(inode, handle, path, - dealloc); + dealloc, et); if (ret) mlog_errno(ret); goto out; @@ -2641,7 +2937,7 @@ rightmost_no_delete: */ try_rotate: ret = __ocfs2_rotate_tree_left(inode, handle, orig_credits, path, - dealloc, &restart_path); + dealloc, &restart_path, et); if (ret && ret != -EAGAIN) { mlog_errno(ret); goto out; @@ -2653,7 +2949,7 @@ try_rotate: ret = __ocfs2_rotate_tree_left(inode, handle, orig_credits, tmp_path, dealloc, - &restart_path); + &restart_path, et); if (ret && ret != -EAGAIN) { mlog_errno(ret); goto out; @@ -2939,6 +3235,7 @@ static int ocfs2_merge_rec_left(struct inode *inode, handle_t *handle, struct ocfs2_extent_rec *split_rec, struct ocfs2_cached_dealloc_ctxt *dealloc, + struct ocfs2_extent_tree *et, int index) { int ret, i, subtree_index = 0, has_empty_extent = 0; @@ -3059,7 +3356,8 @@ static int ocfs2_merge_rec_left(struct inode *inode, le16_to_cpu(el->l_next_free_rec) == 1) { ret = ocfs2_remove_rightmost_path(inode, handle, - right_path, dealloc); + right_path, + dealloc, et); if (ret) { mlog_errno(ret); goto out; @@ -3086,7 +3384,8 @@ static int ocfs2_try_to_merge_extent(struct inode *inode, int split_index, struct ocfs2_extent_rec *split_rec, struct ocfs2_cached_dealloc_ctxt *dealloc, - struct ocfs2_merge_ctxt *ctxt) + struct ocfs2_merge_ctxt *ctxt, + struct ocfs2_extent_tree *et) { int ret = 0; @@ -3104,7 +3403,7 @@ static int ocfs2_try_to_merge_extent(struct inode *inode, * illegal. */ ret = ocfs2_rotate_tree_left(inode, handle, path, - dealloc); + dealloc, et); if (ret) { mlog_errno(ret); goto out; @@ -3147,7 +3446,8 @@ static int ocfs2_try_to_merge_extent(struct inode *inode, BUG_ON(!ocfs2_is_empty_extent(&el->l_recs[0])); /* The merge left us with an empty extent, remove it. */ - ret = ocfs2_rotate_tree_left(inode, handle, path, dealloc); + ret = ocfs2_rotate_tree_left(inode, handle, path, + dealloc, et); if (ret) { mlog_errno(ret); goto out; @@ -3161,7 +3461,7 @@ static int ocfs2_try_to_merge_extent(struct inode *inode, */ ret = ocfs2_merge_rec_left(inode, path, handle, rec, - dealloc, + dealloc, et, split_index); if (ret) { @@ -3170,7 +3470,7 @@ static int ocfs2_try_to_merge_extent(struct inode *inode, } ret = ocfs2_rotate_tree_left(inode, handle, path, - dealloc); + dealloc, et); /* * Error from this last rotate is not critical, so * print but don't bubble it up. @@ -3190,7 +3490,7 @@ static int ocfs2_try_to_merge_extent(struct inode *inode, ret = ocfs2_merge_rec_left(inode, path, handle, split_rec, - dealloc, + dealloc, et, split_index); if (ret) { mlog_errno(ret); @@ -3213,7 +3513,7 @@ static int ocfs2_try_to_merge_extent(struct inode *inode, * our leaf. Try to rotate it away. */ ret = ocfs2_rotate_tree_left(inode, handle, path, - dealloc); + dealloc, et); if (ret) mlog_errno(ret); ret = 0; @@ -3347,16 +3647,6 @@ rotate: ocfs2_rotate_leaf(el, insert_rec); } -static inline void ocfs2_update_dinode_clusters(struct inode *inode, - struct ocfs2_dinode *di, - u32 clusters) -{ - le32_add_cpu(&di->i_clusters, clusters); - spin_lock(&OCFS2_I(inode)->ip_lock); - OCFS2_I(inode)->ip_clusters = le32_to_cpu(di->i_clusters); - spin_unlock(&OCFS2_I(inode)->ip_lock); -} - static void ocfs2_adjust_rightmost_records(struct inode *inode, handle_t *handle, struct ocfs2_path *path, @@ -3558,8 +3848,8 @@ static void ocfs2_split_record(struct inode *inode, } /* - * This function only does inserts on an allocation b-tree. For dinode - * lists, ocfs2_insert_at_leaf() is called directly. + * This function only does inserts on an allocation b-tree. For tree + * depth = 0, ocfs2_insert_at_leaf() is called directly. * * right_path is the path we want to do the actual insert * in. left_path should only be passed in if we need to update that @@ -3656,7 +3946,7 @@ out: static int ocfs2_do_insert_extent(struct inode *inode, handle_t *handle, - struct buffer_head *di_bh, + struct ocfs2_extent_tree *et, struct ocfs2_extent_rec *insert_rec, struct ocfs2_insert_type *type) { @@ -3664,13 +3954,11 @@ static int ocfs2_do_insert_extent(struct inode *inode, u32 cpos; struct ocfs2_path *right_path = NULL; struct ocfs2_path *left_path = NULL; - struct ocfs2_dinode *di; struct ocfs2_extent_list *el; - di = (struct ocfs2_dinode *) di_bh->b_data; - el = &di->id2.i_list; + el = et->et_root_el; - ret = ocfs2_journal_access(handle, inode, di_bh, + ret = ocfs2_journal_access(handle, inode, et->et_root_bh, OCFS2_JOURNAL_ACCESS_WRITE); if (ret) { mlog_errno(ret); @@ -3682,7 +3970,7 @@ static int ocfs2_do_insert_extent(struct inode *inode, goto out_update_clusters; } - right_path = ocfs2_new_inode_path(di_bh); + right_path = ocfs2_new_path(et->et_root_bh, et->et_root_el); if (!right_path) { ret = -ENOMEM; mlog_errno(ret); @@ -3732,7 +4020,7 @@ static int ocfs2_do_insert_extent(struct inode *inode, * ocfs2_rotate_tree_right() might have extended the * transaction without re-journaling our tree root. */ - ret = ocfs2_journal_access(handle, inode, di_bh, + ret = ocfs2_journal_access(handle, inode, et->et_root_bh, OCFS2_JOURNAL_ACCESS_WRITE); if (ret) { mlog_errno(ret); @@ -3757,10 +4045,10 @@ static int ocfs2_do_insert_extent(struct inode *inode, out_update_clusters: if (type->ins_split == SPLIT_NONE) - ocfs2_update_dinode_clusters(inode, di, - le16_to_cpu(insert_rec->e_leaf_clusters)); + ocfs2_et_update_clusters(inode, et, + le16_to_cpu(insert_rec->e_leaf_clusters)); - ret = ocfs2_journal_dirty(handle, di_bh); + ret = ocfs2_journal_dirty(handle, et->et_root_bh); if (ret) mlog_errno(ret); @@ -3890,7 +4178,8 @@ out: static void ocfs2_figure_contig_type(struct inode *inode, struct ocfs2_insert_type *insert, struct ocfs2_extent_list *el, - struct ocfs2_extent_rec *insert_rec) + struct ocfs2_extent_rec *insert_rec, + struct ocfs2_extent_tree *et) { int i; enum ocfs2_contig_type contig_type = CONTIG_NONE; @@ -3906,6 +4195,21 @@ static void ocfs2_figure_contig_type(struct inode *inode, } } insert->ins_contig = contig_type; + + if (insert->ins_contig != CONTIG_NONE) { + struct ocfs2_extent_rec *rec = + &el->l_recs[insert->ins_contig_index]; + unsigned int len = le16_to_cpu(rec->e_leaf_clusters) + + le16_to_cpu(insert_rec->e_leaf_clusters); + + /* + * Caller might want us to limit the size of extents, don't + * calculate contiguousness if we might exceed that limit. + */ + if (et->et_max_leaf_clusters && + (len > et->et_max_leaf_clusters)) + insert->ins_contig = CONTIG_NONE; + } } /* @@ -3914,8 +4218,8 @@ static void ocfs2_figure_contig_type(struct inode *inode, * ocfs2_figure_appending_type() will figure out whether we'll have to * insert at the tail of the rightmost leaf. * - * This should also work against the dinode list for tree's with 0 - * depth. If we consider the dinode list to be the rightmost leaf node + * This should also work against the root extent list for tree's with 0 + * depth. If we consider the root extent list to be the rightmost leaf node * then the logic here makes sense. */ static void ocfs2_figure_appending_type(struct ocfs2_insert_type *insert, @@ -3966,14 +4270,13 @@ set_tail_append: * structure. */ static int ocfs2_figure_insert_type(struct inode *inode, - struct buffer_head *di_bh, + struct ocfs2_extent_tree *et, struct buffer_head **last_eb_bh, struct ocfs2_extent_rec *insert_rec, int *free_records, struct ocfs2_insert_type *insert) { int ret; - struct ocfs2_dinode *di = (struct ocfs2_dinode *)di_bh->b_data; struct ocfs2_extent_block *eb; struct ocfs2_extent_list *el; struct ocfs2_path *path = NULL; @@ -3981,7 +4284,7 @@ static int ocfs2_figure_insert_type(struct inode *inode, insert->ins_split = SPLIT_NONE; - el = &di->id2.i_list; + el = et->et_root_el; insert->ins_tree_depth = le16_to_cpu(el->l_tree_depth); if (el->l_tree_depth) { @@ -3991,9 +4294,7 @@ static int ocfs2_figure_insert_type(struct inode *inode, * ocfs2_figure_insert_type() and ocfs2_add_branch() * may want it later. */ - ret = ocfs2_read_block(OCFS2_SB(inode->i_sb), - le64_to_cpu(di->i_last_eb_blk), &bh, - OCFS2_BH_CACHED, inode); + ret = ocfs2_read_block(inode, ocfs2_et_get_last_eb_blk(et), &bh); if (ret) { mlog_exit(ret); goto out; @@ -4014,12 +4315,12 @@ static int ocfs2_figure_insert_type(struct inode *inode, le16_to_cpu(el->l_next_free_rec); if (!insert->ins_tree_depth) { - ocfs2_figure_contig_type(inode, insert, el, insert_rec); + ocfs2_figure_contig_type(inode, insert, el, insert_rec, et); ocfs2_figure_appending_type(insert, el, insert_rec); return 0; } - path = ocfs2_new_inode_path(di_bh); + path = ocfs2_new_path(et->et_root_bh, et->et_root_el); if (!path) { ret = -ENOMEM; mlog_errno(ret); @@ -4048,7 +4349,7 @@ static int ocfs2_figure_insert_type(struct inode *inode, * into two types of appends: simple record append, or a * rotate inside the tail leaf. */ - ocfs2_figure_contig_type(inode, insert, el, insert_rec); + ocfs2_figure_contig_type(inode, insert, el, insert_rec, et); /* * The insert code isn't quite ready to deal with all cases of @@ -4069,7 +4370,8 @@ static int ocfs2_figure_insert_type(struct inode *inode, * the case that we're doing a tail append, so maybe we can * take advantage of that information somehow. */ - if (le64_to_cpu(di->i_last_eb_blk) == path_leaf_bh(path)->b_blocknr) { + if (ocfs2_et_get_last_eb_blk(et) == + path_leaf_bh(path)->b_blocknr) { /* * Ok, ocfs2_find_path() returned us the rightmost * tree path. This might be an appending insert. There are @@ -4099,7 +4401,7 @@ out: int ocfs2_insert_extent(struct ocfs2_super *osb, handle_t *handle, struct inode *inode, - struct buffer_head *fe_bh, + struct ocfs2_extent_tree *et, u32 cpos, u64 start_blk, u32 new_clusters, @@ -4112,26 +4414,21 @@ int ocfs2_insert_extent(struct ocfs2_super *osb, struct ocfs2_insert_type insert = {0, }; struct ocfs2_extent_rec rec; - BUG_ON(OCFS2_I(inode)->ip_dyn_features & OCFS2_INLINE_DATA_FL); - mlog(0, "add %u clusters at position %u to inode %llu\n", new_clusters, cpos, (unsigned long long)OCFS2_I(inode)->ip_blkno); - mlog_bug_on_msg(!ocfs2_sparse_alloc(osb) && - (OCFS2_I(inode)->ip_clusters != cpos), - "Device %s, asking for sparse allocation: inode %llu, " - "cpos %u, clusters %u\n", - osb->dev_str, - (unsigned long long)OCFS2_I(inode)->ip_blkno, cpos, - OCFS2_I(inode)->ip_clusters); - memset(&rec, 0, sizeof(rec)); rec.e_cpos = cpu_to_le32(cpos); rec.e_blkno = cpu_to_le64(start_blk); rec.e_leaf_clusters = cpu_to_le16(new_clusters); rec.e_flags = flags; + status = ocfs2_et_insert_check(inode, et, &rec); + if (status) { + mlog_errno(status); + goto bail; + } - status = ocfs2_figure_insert_type(inode, fe_bh, &last_eb_bh, &rec, + status = ocfs2_figure_insert_type(inode, et, &last_eb_bh, &rec, &free_records, &insert); if (status < 0) { mlog_errno(status); @@ -4145,7 +4442,7 @@ int ocfs2_insert_extent(struct ocfs2_super *osb, free_records, insert.ins_tree_depth); if (insert.ins_contig == CONTIG_NONE && free_records == 0) { - status = ocfs2_grow_tree(inode, handle, fe_bh, + status = ocfs2_grow_tree(inode, handle, et, &insert.ins_tree_depth, &last_eb_bh, meta_ac); if (status) { @@ -4155,17 +4452,124 @@ int ocfs2_insert_extent(struct ocfs2_super *osb, } /* Finally, we can add clusters. This might rotate the tree for us. */ - status = ocfs2_do_insert_extent(inode, handle, fe_bh, &rec, &insert); + status = ocfs2_do_insert_extent(inode, handle, et, &rec, &insert); if (status < 0) mlog_errno(status); - else + else if (et->et_ops == &ocfs2_dinode_et_ops) ocfs2_extent_map_insert_rec(inode, &rec); bail: - if (last_eb_bh) - brelse(last_eb_bh); + brelse(last_eb_bh); + + mlog_exit(status); + return status; +} + +/* + * Allcate and add clusters into the extent b-tree. + * The new clusters(clusters_to_add) will be inserted at logical_offset. + * The extent b-tree's root is specified by et, and + * it is not limited to the file storage. Any extent tree can use this + * function if it implements the proper ocfs2_extent_tree. + */ +int ocfs2_add_clusters_in_btree(struct ocfs2_super *osb, + struct inode *inode, + u32 *logical_offset, + u32 clusters_to_add, + int mark_unwritten, + struct ocfs2_extent_tree *et, + handle_t *handle, + struct ocfs2_alloc_context *data_ac, + struct ocfs2_alloc_context *meta_ac, + enum ocfs2_alloc_restarted *reason_ret) +{ + int status = 0; + int free_extents; + enum ocfs2_alloc_restarted reason = RESTART_NONE; + u32 bit_off, num_bits; + u64 block; + u8 flags = 0; + + BUG_ON(!clusters_to_add); + + if (mark_unwritten) + flags = OCFS2_EXT_UNWRITTEN; + + free_extents = ocfs2_num_free_extents(osb, inode, et); + if (free_extents < 0) { + status = free_extents; + mlog_errno(status); + goto leave; + } + + /* there are two cases which could cause us to EAGAIN in the + * we-need-more-metadata case: + * 1) we haven't reserved *any* + * 2) we are so fragmented, we've needed to add metadata too + * many times. */ + if (!free_extents && !meta_ac) { + mlog(0, "we haven't reserved any metadata!\n"); + status = -EAGAIN; + reason = RESTART_META; + goto leave; + } else if ((!free_extents) + && (ocfs2_alloc_context_bits_left(meta_ac) + < ocfs2_extend_meta_needed(et->et_root_el))) { + mlog(0, "filesystem is really fragmented...\n"); + status = -EAGAIN; + reason = RESTART_META; + goto leave; + } + + status = __ocfs2_claim_clusters(osb, handle, data_ac, 1, + clusters_to_add, &bit_off, &num_bits); + if (status < 0) { + if (status != -ENOSPC) + mlog_errno(status); + goto leave; + } + + BUG_ON(num_bits > clusters_to_add); + /* reserve our write early -- insert_extent may update the inode */ + status = ocfs2_journal_access(handle, inode, et->et_root_bh, + OCFS2_JOURNAL_ACCESS_WRITE); + if (status < 0) { + mlog_errno(status); + goto leave; + } + + block = ocfs2_clusters_to_blocks(osb->sb, bit_off); + mlog(0, "Allocating %u clusters at block %u for inode %llu\n", + num_bits, bit_off, (unsigned long long)OCFS2_I(inode)->ip_blkno); + status = ocfs2_insert_extent(osb, handle, inode, et, + *logical_offset, block, + num_bits, flags, meta_ac); + if (status < 0) { + mlog_errno(status); + goto leave; + } + + status = ocfs2_journal_dirty(handle, et->et_root_bh); + if (status < 0) { + mlog_errno(status); + goto leave; + } + + clusters_to_add -= num_bits; + *logical_offset += num_bits; + + if (clusters_to_add) { + mlog(0, "need to alloc once more, wanted = %u\n", + clusters_to_add); + status = -EAGAIN; + reason = RESTART_TRANS; + } + +leave: mlog_exit(status); + if (reason_ret) + *reason_ret = reason; return status; } @@ -4192,7 +4596,7 @@ static void ocfs2_make_right_split_rec(struct super_block *sb, static int ocfs2_split_and_insert(struct inode *inode, handle_t *handle, struct ocfs2_path *path, - struct buffer_head *di_bh, + struct ocfs2_extent_tree *et, struct buffer_head **last_eb_bh, int split_index, struct ocfs2_extent_rec *orig_split_rec, @@ -4206,7 +4610,6 @@ static int ocfs2_split_and_insert(struct inode *inode, struct ocfs2_extent_rec split_rec = *orig_split_rec; struct ocfs2_insert_type insert; struct ocfs2_extent_block *eb; - struct ocfs2_dinode *di; leftright: /* @@ -4215,8 +4618,7 @@ leftright: */ rec = path_leaf_el(path)->l_recs[split_index]; - di = (struct ocfs2_dinode *)di_bh->b_data; - rightmost_el = &di->id2.i_list; + rightmost_el = et->et_root_el; depth = le16_to_cpu(rightmost_el->l_tree_depth); if (depth) { @@ -4227,8 +4629,8 @@ leftright: if (le16_to_cpu(rightmost_el->l_next_free_rec) == le16_to_cpu(rightmost_el->l_count)) { - ret = ocfs2_grow_tree(inode, handle, di_bh, &depth, last_eb_bh, - meta_ac); + ret = ocfs2_grow_tree(inode, handle, et, + &depth, last_eb_bh, meta_ac); if (ret) { mlog_errno(ret); goto out; @@ -4265,8 +4667,7 @@ leftright: do_leftright = 1; } - ret = ocfs2_do_insert_extent(inode, handle, di_bh, &split_rec, - &insert); + ret = ocfs2_do_insert_extent(inode, handle, et, &split_rec, &insert); if (ret) { mlog_errno(ret); goto out; @@ -4308,8 +4709,9 @@ out: * of the tree is required. All other cases will degrade into a less * optimal tree layout. * - * last_eb_bh should be the rightmost leaf block for any inode with a - * btree. Since a split may grow the tree or a merge might shrink it, the caller cannot trust the contents of that buffer after this call. + * last_eb_bh should be the rightmost leaf block for any extent + * btree. Since a split may grow the tree or a merge might shrink it, + * the caller cannot trust the contents of that buffer after this call. * * This code is optimized for readability - several passes might be * made over certain portions of the tree. All of those blocks will @@ -4317,7 +4719,7 @@ out: * extra overhead is not expressed in terms of disk reads. */ static int __ocfs2_mark_extent_written(struct inode *inode, - struct buffer_head *di_bh, + struct ocfs2_extent_tree *et, handle_t *handle, struct ocfs2_path *path, int split_index, @@ -4357,11 +4759,9 @@ static int __ocfs2_mark_extent_written(struct inode *inode, */ if (path->p_tree_depth) { struct ocfs2_extent_block *eb; - struct ocfs2_dinode *di = (struct ocfs2_dinode *)di_bh->b_data; - ret = ocfs2_read_block(OCFS2_SB(inode->i_sb), - le64_to_cpu(di->i_last_eb_blk), - &last_eb_bh, OCFS2_BH_CACHED, inode); + ret = ocfs2_read_block(inode, ocfs2_et_get_last_eb_blk(et), + &last_eb_bh); if (ret) { mlog_exit(ret); goto out; @@ -4394,7 +4794,7 @@ static int __ocfs2_mark_extent_written(struct inode *inode, if (ctxt.c_split_covers_rec) el->l_recs[split_index] = *split_rec; else - ret = ocfs2_split_and_insert(inode, handle, path, di_bh, + ret = ocfs2_split_and_insert(inode, handle, path, et, &last_eb_bh, split_index, split_rec, meta_ac); if (ret) @@ -4402,7 +4802,7 @@ static int __ocfs2_mark_extent_written(struct inode *inode, } else { ret = ocfs2_try_to_merge_extent(inode, handle, path, split_index, split_rec, - dealloc, &ctxt); + dealloc, &ctxt, et); if (ret) mlog_errno(ret); } @@ -4420,7 +4820,8 @@ out: * * The caller is responsible for passing down meta_ac if we'll need it. */ -int ocfs2_mark_extent_written(struct inode *inode, struct buffer_head *di_bh, +int ocfs2_mark_extent_written(struct inode *inode, + struct ocfs2_extent_tree *et, handle_t *handle, u32 cpos, u32 len, u32 phys, struct ocfs2_alloc_context *meta_ac, struct ocfs2_cached_dealloc_ctxt *dealloc) @@ -4446,10 +4847,14 @@ int ocfs2_mark_extent_written(struct inode *inode, struct buffer_head *di_bh, /* * XXX: This should be fixed up so that we just re-insert the * next extent records. + * + * XXX: This is a hack on the extent tree, maybe it should be + * an op? */ - ocfs2_extent_map_trunc(inode, 0); + if (et->et_ops == &ocfs2_dinode_et_ops) + ocfs2_extent_map_trunc(inode, 0); - left_path = ocfs2_new_inode_path(di_bh); + left_path = ocfs2_new_path(et->et_root_bh, et->et_root_el); if (!left_path) { ret = -ENOMEM; mlog_errno(ret); @@ -4480,8 +4885,9 @@ int ocfs2_mark_extent_written(struct inode *inode, struct buffer_head *di_bh, split_rec.e_flags = path_leaf_el(left_path)->l_recs[index].e_flags; split_rec.e_flags &= ~OCFS2_EXT_UNWRITTEN; - ret = __ocfs2_mark_extent_written(inode, di_bh, handle, left_path, - index, &split_rec, meta_ac, dealloc); + ret = __ocfs2_mark_extent_written(inode, et, handle, left_path, + index, &split_rec, meta_ac, + dealloc); if (ret) mlog_errno(ret); @@ -4490,13 +4896,12 @@ out: return ret; } -static int ocfs2_split_tree(struct inode *inode, struct buffer_head *di_bh, +static int ocfs2_split_tree(struct inode *inode, struct ocfs2_extent_tree *et, handle_t *handle, struct ocfs2_path *path, int index, u32 new_range, struct ocfs2_alloc_context *meta_ac) { int ret, depth, credits = handle->h_buffer_credits; - struct ocfs2_dinode *di = (struct ocfs2_dinode *)di_bh->b_data; struct buffer_head *last_eb_bh = NULL; struct ocfs2_extent_block *eb; struct ocfs2_extent_list *rightmost_el, *el; @@ -4513,9 +4918,8 @@ static int ocfs2_split_tree(struct inode *inode, struct buffer_head *di_bh, depth = path->p_tree_depth; if (depth > 0) { - ret = ocfs2_read_block(OCFS2_SB(inode->i_sb), - le64_to_cpu(di->i_last_eb_blk), - &last_eb_bh, OCFS2_BH_CACHED, inode); + ret = ocfs2_read_block(inode, ocfs2_et_get_last_eb_blk(et), + &last_eb_bh); if (ret < 0) { mlog_errno(ret); goto out; @@ -4526,7 +4930,8 @@ static int ocfs2_split_tree(struct inode *inode, struct buffer_head *di_bh, } else rightmost_el = path_leaf_el(path); - credits += path->p_tree_depth + ocfs2_extend_meta_needed(di); + credits += path->p_tree_depth + + ocfs2_extend_meta_needed(et->et_root_el); ret = ocfs2_extend_trans(handle, credits); if (ret) { mlog_errno(ret); @@ -4535,7 +4940,7 @@ static int ocfs2_split_tree(struct inode *inode, struct buffer_head *di_bh, if (le16_to_cpu(rightmost_el->l_next_free_rec) == le16_to_cpu(rightmost_el->l_count)) { - ret = ocfs2_grow_tree(inode, handle, di_bh, &depth, &last_eb_bh, + ret = ocfs2_grow_tree(inode, handle, et, &depth, &last_eb_bh, meta_ac); if (ret) { mlog_errno(ret); @@ -4549,7 +4954,7 @@ static int ocfs2_split_tree(struct inode *inode, struct buffer_head *di_bh, insert.ins_split = SPLIT_RIGHT; insert.ins_tree_depth = depth; - ret = ocfs2_do_insert_extent(inode, handle, di_bh, &split_rec, &insert); + ret = ocfs2_do_insert_extent(inode, handle, et, &split_rec, &insert); if (ret) mlog_errno(ret); @@ -4561,7 +4966,8 @@ out: static int ocfs2_truncate_rec(struct inode *inode, handle_t *handle, struct ocfs2_path *path, int index, struct ocfs2_cached_dealloc_ctxt *dealloc, - u32 cpos, u32 len) + u32 cpos, u32 len, + struct ocfs2_extent_tree *et) { int ret; u32 left_cpos, rec_range, trunc_range; @@ -4573,7 +4979,7 @@ static int ocfs2_truncate_rec(struct inode *inode, handle_t *handle, struct ocfs2_extent_block *eb; if (ocfs2_is_empty_extent(&el->l_recs[0]) && index > 0) { - ret = ocfs2_rotate_tree_left(inode, handle, path, dealloc); + ret = ocfs2_rotate_tree_left(inode, handle, path, dealloc, et); if (ret) { mlog_errno(ret); goto out; @@ -4704,7 +5110,7 @@ static int ocfs2_truncate_rec(struct inode *inode, handle_t *handle, ocfs2_journal_dirty(handle, path_leaf_bh(path)); - ret = ocfs2_rotate_tree_left(inode, handle, path, dealloc); + ret = ocfs2_rotate_tree_left(inode, handle, path, dealloc, et); if (ret) { mlog_errno(ret); goto out; @@ -4715,7 +5121,8 @@ out: return ret; } -int ocfs2_remove_extent(struct inode *inode, struct buffer_head *di_bh, +int ocfs2_remove_extent(struct inode *inode, + struct ocfs2_extent_tree *et, u32 cpos, u32 len, handle_t *handle, struct ocfs2_alloc_context *meta_ac, struct ocfs2_cached_dealloc_ctxt *dealloc) @@ -4724,11 +5131,11 @@ int ocfs2_remove_extent(struct inode *inode, struct buffer_head *di_bh, u32 rec_range, trunc_range; struct ocfs2_extent_rec *rec; struct ocfs2_extent_list *el; - struct ocfs2_path *path; + struct ocfs2_path *path = NULL; ocfs2_extent_map_trunc(inode, 0); - path = ocfs2_new_inode_path(di_bh); + path = ocfs2_new_path(et->et_root_bh, et->et_root_el); if (!path) { ret = -ENOMEM; mlog_errno(ret); @@ -4781,13 +5188,13 @@ int ocfs2_remove_extent(struct inode *inode, struct buffer_head *di_bh, if (le32_to_cpu(rec->e_cpos) == cpos || rec_range == trunc_range) { ret = ocfs2_truncate_rec(inode, handle, path, index, dealloc, - cpos, len); + cpos, len, et); if (ret) { mlog_errno(ret); goto out; } } else { - ret = ocfs2_split_tree(inode, di_bh, handle, path, index, + ret = ocfs2_split_tree(inode, et, handle, path, index, trunc_range, meta_ac); if (ret) { mlog_errno(ret); @@ -4836,7 +5243,7 @@ int ocfs2_remove_extent(struct inode *inode, struct buffer_head *di_bh, } ret = ocfs2_truncate_rec(inode, handle, path, index, dealloc, - cpos, len); + cpos, len, et); if (ret) { mlog_errno(ret); goto out; @@ -5179,8 +5586,7 @@ static int ocfs2_get_truncate_log_info(struct ocfs2_super *osb, goto bail; } - status = ocfs2_read_block(osb, OCFS2_I(inode)->ip_blkno, &bh, - OCFS2_BH_CACHED, inode); + status = ocfs2_read_block(inode, OCFS2_I(inode)->ip_blkno, &bh); if (status < 0) { iput(inode); mlog_errno(status); @@ -5255,8 +5661,7 @@ int ocfs2_begin_truncate_log_recovery(struct ocfs2_super *osb, bail: if (tl_inode) iput(tl_inode); - if (tl_bh) - brelse(tl_bh); + brelse(tl_bh); if (status < 0 && (*tl_copy)) { kfree(*tl_copy); @@ -5999,20 +6404,13 @@ bail: return status; } -static int ocfs2_writeback_zero_func(handle_t *handle, struct buffer_head *bh) +static int ocfs2_zero_func(handle_t *handle, struct buffer_head *bh) { set_buffer_uptodate(bh); mark_buffer_dirty(bh); return 0; } -static int ocfs2_ordered_zero_func(handle_t *handle, struct buffer_head *bh) -{ - set_buffer_uptodate(bh); - mark_buffer_dirty(bh); - return ocfs2_journal_dirty_data(handle, bh); -} - static void ocfs2_map_and_dirty_page(struct inode *inode, handle_t *handle, unsigned int from, unsigned int to, struct page *page, int zero, u64 *phys) @@ -6031,17 +6429,18 @@ static void ocfs2_map_and_dirty_page(struct inode *inode, handle_t *handle, * here if they aren't - ocfs2_map_page_blocks() * might've skipped some */ - if (ocfs2_should_order_data(inode)) { - ret = walk_page_buffers(handle, - page_buffers(page), - from, to, &partial, - ocfs2_ordered_zero_func); - if (ret < 0) - mlog_errno(ret); - } else { + ret = walk_page_buffers(handle, page_buffers(page), + from, to, &partial, + ocfs2_zero_func); + if (ret < 0) + mlog_errno(ret); + else if (ocfs2_should_order_data(inode)) { + ret = ocfs2_jbd2_file_inode(handle, inode); +#ifdef CONFIG_OCFS2_COMPAT_JBD ret = walk_page_buffers(handle, page_buffers(page), from, to, &partial, - ocfs2_writeback_zero_func); + ocfs2_journal_dirty_data); +#endif if (ret < 0) mlog_errno(ret); } @@ -6206,20 +6605,29 @@ out: return ret; } -static void ocfs2_zero_dinode_id2(struct inode *inode, struct ocfs2_dinode *di) +static void ocfs2_zero_dinode_id2_with_xattr(struct inode *inode, + struct ocfs2_dinode *di) { unsigned int blocksize = 1 << inode->i_sb->s_blocksize_bits; + unsigned int xattrsize = le16_to_cpu(di->i_xattr_inline_size); - memset(&di->id2, 0, blocksize - offsetof(struct ocfs2_dinode, id2)); + if (le16_to_cpu(di->i_dyn_features) & OCFS2_INLINE_XATTR_FL) + memset(&di->id2, 0, blocksize - + offsetof(struct ocfs2_dinode, id2) - + xattrsize); + else + memset(&di->id2, 0, blocksize - + offsetof(struct ocfs2_dinode, id2)); } void ocfs2_dinode_new_extent_list(struct inode *inode, struct ocfs2_dinode *di) { - ocfs2_zero_dinode_id2(inode, di); + ocfs2_zero_dinode_id2_with_xattr(inode, di); di->id2.i_list.l_tree_depth = 0; di->id2.i_list.l_next_free_rec = 0; - di->id2.i_list.l_count = cpu_to_le16(ocfs2_extent_recs_per_inode(inode->i_sb)); + di->id2.i_list.l_count = cpu_to_le16( + ocfs2_extent_recs_per_inode_with_xattr(inode->i_sb, di)); } void ocfs2_set_inode_data_inline(struct inode *inode, struct ocfs2_dinode *di) @@ -6236,9 +6644,10 @@ void ocfs2_set_inode_data_inline(struct inode *inode, struct ocfs2_dinode *di) * We clear the entire i_data structure here so that all * fields can be properly initialized. */ - ocfs2_zero_dinode_id2(inode, di); + ocfs2_zero_dinode_id2_with_xattr(inode, di); - idata->id_count = cpu_to_le16(ocfs2_max_inline_data(inode->i_sb)); + idata->id_count = cpu_to_le16( + ocfs2_max_inline_data_with_xattr(inode->i_sb, di)); } int ocfs2_convert_inline_data_to_extents(struct inode *inode, @@ -6253,6 +6662,7 @@ int ocfs2_convert_inline_data_to_extents(struct inode *inode, struct ocfs2_alloc_context *data_ac = NULL; struct page **pages = NULL; loff_t end = osb->s_clustersize; + struct ocfs2_extent_tree et; has_data = i_size_read(inode) ? 1 : 0; @@ -6352,7 +6762,8 @@ int ocfs2_convert_inline_data_to_extents(struct inode *inode, * this proves to be false, we could always re-build * the in-inode data from our pages. */ - ret = ocfs2_insert_extent(osb, handle, inode, di_bh, + ocfs2_init_dinode_extent_tree(&et, inode, di_bh); + ret = ocfs2_insert_extent(osb, handle, inode, &et, 0, block, 1, 0, NULL); if (ret) { mlog_errno(ret); @@ -6395,13 +6806,14 @@ int ocfs2_commit_truncate(struct ocfs2_super *osb, handle_t *handle = NULL; struct inode *tl_inode = osb->osb_tl_inode; struct ocfs2_path *path = NULL; + struct ocfs2_dinode *di = (struct ocfs2_dinode *)fe_bh->b_data; mlog_entry_void(); new_highest_cpos = ocfs2_clusters_for_bytes(osb->sb, i_size_read(inode)); - path = ocfs2_new_inode_path(fe_bh); + path = ocfs2_new_path(fe_bh, &di->id2.i_list); if (!path) { status = -ENOMEM; mlog_errno(status); @@ -6572,8 +6984,8 @@ int ocfs2_prepare_truncate(struct ocfs2_super *osb, ocfs2_init_dealloc_ctxt(&(*tc)->tc_dealloc); if (fe->id2.i_list.l_tree_depth) { - status = ocfs2_read_block(osb, le64_to_cpu(fe->i_last_eb_blk), - &last_eb_bh, OCFS2_BH_CACHED, inode); + status = ocfs2_read_block(inode, le64_to_cpu(fe->i_last_eb_blk), + &last_eb_bh); if (status < 0) { mlog_errno(status); goto bail; @@ -6686,8 +7098,7 @@ static void ocfs2_free_truncate_context(struct ocfs2_truncate_context *tc) mlog(ML_NOTICE, "Truncate completion has non-empty dealloc context\n"); - if (tc->tc_last_eb_bh) - brelse(tc->tc_last_eb_bh); + brelse(tc->tc_last_eb_bh); kfree(tc); } diff --git a/fs/ocfs2/alloc.h b/fs/ocfs2/alloc.h index 60cd3d59230c..70257c84cfbe 100644 --- a/fs/ocfs2/alloc.h +++ b/fs/ocfs2/alloc.h @@ -26,30 +26,102 @@ #ifndef OCFS2_ALLOC_H #define OCFS2_ALLOC_H + +/* + * For xattr tree leaf, we limit the leaf byte size to be 64K. + */ +#define OCFS2_MAX_XATTR_TREE_LEAF_SIZE 65536 + +/* + * ocfs2_extent_tree and ocfs2_extent_tree_operations are used to abstract + * the b-tree operations in ocfs2. Now all the b-tree operations are not + * limited to ocfs2_dinode only. Any data which need to allocate clusters + * to store can use b-tree. And it only needs to implement its ocfs2_extent_tree + * and operation. + * + * ocfs2_extent_tree becomes the first-class object for extent tree + * manipulation. Callers of the alloc.c code need to fill it via one of + * the ocfs2_init_*_extent_tree() operations below. + * + * ocfs2_extent_tree contains info for the root of the b-tree, it must have a + * root ocfs2_extent_list and a root_bh so that they can be used in the b-tree + * functions. + * ocfs2_extent_tree_operations abstract the normal operations we do for + * the root of extent b-tree. + */ +struct ocfs2_extent_tree_operations; +struct ocfs2_extent_tree { + struct ocfs2_extent_tree_operations *et_ops; + struct buffer_head *et_root_bh; + struct ocfs2_extent_list *et_root_el; + void *et_object; + unsigned int et_max_leaf_clusters; +}; + +/* + * ocfs2_init_*_extent_tree() will fill an ocfs2_extent_tree from the + * specified object buffer. + */ +void ocfs2_init_dinode_extent_tree(struct ocfs2_extent_tree *et, + struct inode *inode, + struct buffer_head *bh); +void ocfs2_init_xattr_tree_extent_tree(struct ocfs2_extent_tree *et, + struct inode *inode, + struct buffer_head *bh); +void ocfs2_init_xattr_value_extent_tree(struct ocfs2_extent_tree *et, + struct inode *inode, + struct buffer_head *bh, + struct ocfs2_xattr_value_root *xv); + struct ocfs2_alloc_context; int ocfs2_insert_extent(struct ocfs2_super *osb, handle_t *handle, struct inode *inode, - struct buffer_head *fe_bh, + struct ocfs2_extent_tree *et, u32 cpos, u64 start_blk, u32 new_clusters, u8 flags, struct ocfs2_alloc_context *meta_ac); + +enum ocfs2_alloc_restarted { + RESTART_NONE = 0, + RESTART_TRANS, + RESTART_META +}; +int ocfs2_add_clusters_in_btree(struct ocfs2_super *osb, + struct inode *inode, + u32 *logical_offset, + u32 clusters_to_add, + int mark_unwritten, + struct ocfs2_extent_tree *et, + handle_t *handle, + struct ocfs2_alloc_context *data_ac, + struct ocfs2_alloc_context *meta_ac, + enum ocfs2_alloc_restarted *reason_ret); struct ocfs2_cached_dealloc_ctxt; -int ocfs2_mark_extent_written(struct inode *inode, struct buffer_head *di_bh, +int ocfs2_mark_extent_written(struct inode *inode, + struct ocfs2_extent_tree *et, handle_t *handle, u32 cpos, u32 len, u32 phys, struct ocfs2_alloc_context *meta_ac, struct ocfs2_cached_dealloc_ctxt *dealloc); -int ocfs2_remove_extent(struct inode *inode, struct buffer_head *di_bh, +int ocfs2_remove_extent(struct inode *inode, + struct ocfs2_extent_tree *et, u32 cpos, u32 len, handle_t *handle, struct ocfs2_alloc_context *meta_ac, struct ocfs2_cached_dealloc_ctxt *dealloc); int ocfs2_num_free_extents(struct ocfs2_super *osb, struct inode *inode, - struct ocfs2_dinode *fe); -/* how many new metadata chunks would an allocation need at maximum? */ -static inline int ocfs2_extend_meta_needed(struct ocfs2_dinode *fe) + struct ocfs2_extent_tree *et); + +/* + * how many new metadata chunks would an allocation need at maximum? + * + * Please note that the caller must make sure that root_el is the root + * of extent tree. So for an inode, it should be &fe->id2.i_list. Otherwise + * the result may be wrong. + */ +static inline int ocfs2_extend_meta_needed(struct ocfs2_extent_list *root_el) { /* * Rather than do all the work of determining how much we need @@ -59,7 +131,7 @@ static inline int ocfs2_extend_meta_needed(struct ocfs2_dinode *fe) * new tree_depth==0 extent_block, and one block at the new * top-of-the tree. */ - return le16_to_cpu(fe->id2.i_list.l_tree_depth) + 2; + return le16_to_cpu(root_el->l_tree_depth) + 2; } void ocfs2_dinode_new_extent_list(struct inode *inode, struct ocfs2_dinode *di); diff --git a/fs/ocfs2/aops.c b/fs/ocfs2/aops.c index a53da1466277..c22543b33420 100644 --- a/fs/ocfs2/aops.c +++ b/fs/ocfs2/aops.c @@ -68,9 +68,7 @@ static int ocfs2_symlink_get_block(struct inode *inode, sector_t iblock, goto bail; } - status = ocfs2_read_block(OCFS2_SB(inode->i_sb), - OCFS2_I(inode)->ip_blkno, - &bh, OCFS2_BH_CACHED, inode); + status = ocfs2_read_block(inode, OCFS2_I(inode)->ip_blkno, &bh); if (status < 0) { mlog_errno(status); goto bail; @@ -128,8 +126,7 @@ static int ocfs2_symlink_get_block(struct inode *inode, sector_t iblock, err = 0; bail: - if (bh) - brelse(bh); + brelse(bh); mlog_exit(err); return err; @@ -261,13 +258,11 @@ static int ocfs2_readpage_inline(struct inode *inode, struct page *page) { int ret; struct buffer_head *di_bh = NULL; - struct ocfs2_super *osb = OCFS2_SB(inode->i_sb); BUG_ON(!PageLocked(page)); BUG_ON(!(OCFS2_I(inode)->ip_dyn_features & OCFS2_INLINE_DATA_FL)); - ret = ocfs2_read_block(osb, OCFS2_I(inode)->ip_blkno, &di_bh, - OCFS2_BH_CACHED, inode); + ret = ocfs2_read_block(inode, OCFS2_I(inode)->ip_blkno, &di_bh); if (ret) { mlog_errno(ret); goto out; @@ -485,11 +480,14 @@ handle_t *ocfs2_start_walk_page_trans(struct inode *inode, } if (ocfs2_should_order_data(inode)) { + ret = ocfs2_jbd2_file_inode(handle, inode); +#ifdef CONFIG_OCFS2_COMPAT_JBD ret = walk_page_buffers(handle, page_buffers(page), from, to, NULL, ocfs2_journal_dirty_data); - if (ret < 0) +#endif + if (ret < 0) mlog_errno(ret); } out: @@ -669,7 +667,7 @@ static void ocfs2_invalidatepage(struct page *page, unsigned long offset) { journal_t *journal = OCFS2_SB(page->mapping->host->i_sb)->journal->j_journal; - journal_invalidatepage(journal, page, offset); + jbd2_journal_invalidatepage(journal, page, offset); } static int ocfs2_releasepage(struct page *page, gfp_t wait) @@ -678,7 +676,7 @@ static int ocfs2_releasepage(struct page *page, gfp_t wait) if (!page_has_buffers(page)) return 0; - return journal_try_to_free_buffers(journal, page, wait); + return jbd2_journal_try_to_free_buffers(journal, page, wait); } static ssize_t ocfs2_direct_IO(int rw, @@ -1074,11 +1072,15 @@ static void ocfs2_write_failure(struct inode *inode, tmppage = wc->w_pages[i]; if (page_has_buffers(tmppage)) { - if (ocfs2_should_order_data(inode)) + if (ocfs2_should_order_data(inode)) { + ocfs2_jbd2_file_inode(wc->w_handle, inode); +#ifdef CONFIG_OCFS2_COMPAT_JBD walk_page_buffers(wc->w_handle, page_buffers(tmppage), from, to, NULL, ocfs2_journal_dirty_data); +#endif + } block_commit_write(tmppage, from, to); } @@ -1242,6 +1244,7 @@ static int ocfs2_write_cluster(struct address_space *mapping, int ret, i, new, should_zero = 0; u64 v_blkno, p_blkno; struct inode *inode = mapping->host; + struct ocfs2_extent_tree et; new = phys == 0 ? 1 : 0; if (new || unwritten) @@ -1255,10 +1258,10 @@ static int ocfs2_write_cluster(struct address_space *mapping, * any additional semaphores or cluster locks. */ tmp_pos = cpos; - ret = ocfs2_do_extend_allocation(OCFS2_SB(inode->i_sb), inode, - &tmp_pos, 1, 0, wc->w_di_bh, - wc->w_handle, data_ac, - meta_ac, NULL); + ret = ocfs2_add_inode_data(OCFS2_SB(inode->i_sb), inode, + &tmp_pos, 1, 0, wc->w_di_bh, + wc->w_handle, data_ac, + meta_ac, NULL); /* * This shouldn't happen because we must have already * calculated the correct meta data allocation required. The @@ -1276,7 +1279,8 @@ static int ocfs2_write_cluster(struct address_space *mapping, goto out; } } else if (unwritten) { - ret = ocfs2_mark_extent_written(inode, wc->w_di_bh, + ocfs2_init_dinode_extent_tree(&et, inode, wc->w_di_bh); + ret = ocfs2_mark_extent_written(inode, &et, wc->w_handle, cpos, 1, phys, meta_ac, &wc->w_dealloc); if (ret < 0) { @@ -1665,6 +1669,7 @@ int ocfs2_write_begin_nolock(struct address_space *mapping, struct ocfs2_alloc_context *data_ac = NULL; struct ocfs2_alloc_context *meta_ac = NULL; handle_t *handle; + struct ocfs2_extent_tree et; ret = ocfs2_alloc_write_ctxt(&wc, osb, pos, len, di_bh); if (ret) { @@ -1712,14 +1717,23 @@ int ocfs2_write_begin_nolock(struct address_space *mapping, * ocfs2_lock_allocators(). It greatly over-estimates * the work to be done. */ - ret = ocfs2_lock_allocators(inode, di, clusters_to_alloc, - extents_to_split, &data_ac, &meta_ac); + mlog(0, "extend inode %llu, i_size = %lld, di->i_clusters = %u," + " clusters_to_add = %u, extents_to_split = %u\n", + (unsigned long long)OCFS2_I(inode)->ip_blkno, + (long long)i_size_read(inode), le32_to_cpu(di->i_clusters), + clusters_to_alloc, extents_to_split); + + ocfs2_init_dinode_extent_tree(&et, inode, wc->w_di_bh); + ret = ocfs2_lock_allocators(inode, &et, + clusters_to_alloc, extents_to_split, + &data_ac, &meta_ac); if (ret) { mlog_errno(ret); goto out; } - credits = ocfs2_calc_extend_credits(inode->i_sb, di, + credits = ocfs2_calc_extend_credits(inode->i_sb, + &di->id2.i_list, clusters_to_alloc); } @@ -1905,11 +1919,15 @@ int ocfs2_write_end_nolock(struct address_space *mapping, } if (page_has_buffers(tmppage)) { - if (ocfs2_should_order_data(inode)) + if (ocfs2_should_order_data(inode)) { + ocfs2_jbd2_file_inode(wc->w_handle, inode); +#ifdef CONFIG_OCFS2_COMPAT_JBD walk_page_buffers(wc->w_handle, page_buffers(tmppage), from, to, NULL, ocfs2_journal_dirty_data); +#endif + } block_commit_write(tmppage, from, to); } } diff --git a/fs/ocfs2/buffer_head_io.c b/fs/ocfs2/buffer_head_io.c index f136639f5b41..7e947c672469 100644 --- a/fs/ocfs2/buffer_head_io.c +++ b/fs/ocfs2/buffer_head_io.c @@ -66,7 +66,7 @@ int ocfs2_write_block(struct ocfs2_super *osb, struct buffer_head *bh, /* remove from dirty list before I/O. */ clear_buffer_dirty(bh); - get_bh(bh); /* for end_buffer_write_sync() */ + get_bh(bh); /* for end_buffer_write_sync() */ bh->b_end_io = end_buffer_write_sync; submit_bh(WRITE, bh); @@ -88,22 +88,103 @@ out: return ret; } -int ocfs2_read_blocks(struct ocfs2_super *osb, u64 block, int nr, - struct buffer_head *bhs[], int flags, - struct inode *inode) +int ocfs2_read_blocks_sync(struct ocfs2_super *osb, u64 block, + unsigned int nr, struct buffer_head *bhs[]) +{ + int status = 0; + unsigned int i; + struct buffer_head *bh; + + if (!nr) { + mlog(ML_BH_IO, "No buffers will be read!\n"); + goto bail; + } + + for (i = 0 ; i < nr ; i++) { + if (bhs[i] == NULL) { + bhs[i] = sb_getblk(osb->sb, block++); + if (bhs[i] == NULL) { + status = -EIO; + mlog_errno(status); + goto bail; + } + } + bh = bhs[i]; + + if (buffer_jbd(bh)) { + mlog(ML_ERROR, + "trying to sync read a jbd " + "managed bh (blocknr = %llu), skipping\n", + (unsigned long long)bh->b_blocknr); + continue; + } + + if (buffer_dirty(bh)) { + /* This should probably be a BUG, or + * at least return an error. */ + mlog(ML_ERROR, + "trying to sync read a dirty " + "buffer! (blocknr = %llu), skipping\n", + (unsigned long long)bh->b_blocknr); + continue; + } + + lock_buffer(bh); + if (buffer_jbd(bh)) { + mlog(ML_ERROR, + "block %llu had the JBD bit set " + "while I was in lock_buffer!", + (unsigned long long)bh->b_blocknr); + BUG(); + } + + clear_buffer_uptodate(bh); + get_bh(bh); /* for end_buffer_read_sync() */ + bh->b_end_io = end_buffer_read_sync; + submit_bh(READ, bh); + } + + for (i = nr; i > 0; i--) { + bh = bhs[i - 1]; + + if (buffer_jbd(bh)) { + mlog(ML_ERROR, + "the journal got the buffer while it was " + "locked for io! (blocknr = %llu)\n", + (unsigned long long)bh->b_blocknr); + BUG(); + } + + wait_on_buffer(bh); + if (!buffer_uptodate(bh)) { + /* Status won't be cleared from here on out, + * so we can safely record this and loop back + * to cleanup the other buffers. */ + status = -EIO; + put_bh(bh); + bhs[i - 1] = NULL; + } + } + +bail: + return status; +} + +int ocfs2_read_blocks(struct inode *inode, u64 block, int nr, + struct buffer_head *bhs[], int flags) { int status = 0; - struct super_block *sb; int i, ignore_cache = 0; struct buffer_head *bh; - mlog_entry("(block=(%llu), nr=(%d), flags=%d, inode=%p)\n", - (unsigned long long)block, nr, flags, inode); + mlog_entry("(inode=%p, block=(%llu), nr=(%d), flags=%d)\n", + inode, (unsigned long long)block, nr, flags); + BUG_ON(!inode); BUG_ON((flags & OCFS2_BH_READAHEAD) && - (!inode || !(flags & OCFS2_BH_CACHED))); + (flags & OCFS2_BH_IGNORE_CACHE)); - if (osb == NULL || osb->sb == NULL || bhs == NULL) { + if (bhs == NULL) { status = -EINVAL; mlog_errno(status); goto bail; @@ -122,26 +203,19 @@ int ocfs2_read_blocks(struct ocfs2_super *osb, u64 block, int nr, goto bail; } - sb = osb->sb; - - if (flags & OCFS2_BH_CACHED && !inode) - flags &= ~OCFS2_BH_CACHED; - - if (inode) - mutex_lock(&OCFS2_I(inode)->ip_io_mutex); + mutex_lock(&OCFS2_I(inode)->ip_io_mutex); for (i = 0 ; i < nr ; i++) { if (bhs[i] == NULL) { - bhs[i] = sb_getblk(sb, block++); + bhs[i] = sb_getblk(inode->i_sb, block++); if (bhs[i] == NULL) { - if (inode) - mutex_unlock(&OCFS2_I(inode)->ip_io_mutex); + mutex_unlock(&OCFS2_I(inode)->ip_io_mutex); status = -EIO; mlog_errno(status); goto bail; } } bh = bhs[i]; - ignore_cache = 0; + ignore_cache = (flags & OCFS2_BH_IGNORE_CACHE); /* There are three read-ahead cases here which we need to * be concerned with. All three assume a buffer has @@ -167,26 +241,27 @@ int ocfs2_read_blocks(struct ocfs2_super *osb, u64 block, int nr, * before our is-it-in-flight check. */ - if (flags & OCFS2_BH_CACHED && - !ocfs2_buffer_uptodate(inode, bh)) { + if (!ignore_cache && !ocfs2_buffer_uptodate(inode, bh)) { mlog(ML_UPTODATE, "bh (%llu), inode %llu not uptodate\n", (unsigned long long)bh->b_blocknr, (unsigned long long)OCFS2_I(inode)->ip_blkno); + /* We're using ignore_cache here to say + * "go to disk" */ ignore_cache = 1; } /* XXX: Can we ever get this and *not* have the cached * flag set? */ if (buffer_jbd(bh)) { - if (!(flags & OCFS2_BH_CACHED) || ignore_cache) + if (ignore_cache) mlog(ML_BH_IO, "trying to sync read a jbd " "managed bh (blocknr = %llu)\n", (unsigned long long)bh->b_blocknr); continue; } - if (!(flags & OCFS2_BH_CACHED) || ignore_cache) { + if (ignore_cache) { if (buffer_dirty(bh)) { /* This should probably be a BUG, or * at least return an error. */ @@ -221,7 +296,7 @@ int ocfs2_read_blocks(struct ocfs2_super *osb, u64 block, int nr, * previously read-ahead buffer may have * completed I/O while we were waiting for the * buffer lock. */ - if ((flags & OCFS2_BH_CACHED) + if (!(flags & OCFS2_BH_IGNORE_CACHE) && !(flags & OCFS2_BH_READAHEAD) && ocfs2_buffer_uptodate(inode, bh)) { unlock_buffer(bh); @@ -265,15 +340,14 @@ int ocfs2_read_blocks(struct ocfs2_super *osb, u64 block, int nr, /* Always set the buffer in the cache, even if it was * a forced read, or read-ahead which hasn't yet * completed. */ - if (inode) - ocfs2_set_buffer_uptodate(inode, bh); + ocfs2_set_buffer_uptodate(inode, bh); } - if (inode) - mutex_unlock(&OCFS2_I(inode)->ip_io_mutex); + mutex_unlock(&OCFS2_I(inode)->ip_io_mutex); mlog(ML_BH_IO, "block=(%llu), nr=(%d), cached=%s, flags=0x%x\n", (unsigned long long)block, nr, - (!(flags & OCFS2_BH_CACHED) || ignore_cache) ? "no" : "yes", flags); + ((flags & OCFS2_BH_IGNORE_CACHE) || ignore_cache) ? "no" : "yes", + flags); bail: diff --git a/fs/ocfs2/buffer_head_io.h b/fs/ocfs2/buffer_head_io.h index c2e78614c3e5..75e1dcb1ade7 100644 --- a/fs/ocfs2/buffer_head_io.h +++ b/fs/ocfs2/buffer_head_io.h @@ -31,31 +31,29 @@ void ocfs2_end_buffer_io_sync(struct buffer_head *bh, int uptodate); -static inline int ocfs2_read_block(struct ocfs2_super *osb, +static inline int ocfs2_read_block(struct inode *inode, u64 off, - struct buffer_head **bh, - int flags, - struct inode *inode); + struct buffer_head **bh); int ocfs2_write_block(struct ocfs2_super *osb, struct buffer_head *bh, struct inode *inode); -int ocfs2_read_blocks(struct ocfs2_super *osb, +int ocfs2_read_blocks(struct inode *inode, u64 block, int nr, struct buffer_head *bhs[], - int flags, - struct inode *inode); + int flags); +int ocfs2_read_blocks_sync(struct ocfs2_super *osb, u64 block, + unsigned int nr, struct buffer_head *bhs[]); int ocfs2_write_super_or_backup(struct ocfs2_super *osb, struct buffer_head *bh); -#define OCFS2_BH_CACHED 1 +#define OCFS2_BH_IGNORE_CACHE 1 #define OCFS2_BH_READAHEAD 8 -static inline int ocfs2_read_block(struct ocfs2_super * osb, u64 off, - struct buffer_head **bh, int flags, - struct inode *inode) +static inline int ocfs2_read_block(struct inode *inode, u64 off, + struct buffer_head **bh) { int status = 0; @@ -65,8 +63,7 @@ static inline int ocfs2_read_block(struct ocfs2_super * osb, u64 off, goto bail; } - status = ocfs2_read_blocks(osb, off, 1, bh, - flags, inode); + status = ocfs2_read_blocks(inode, off, 1, bh, 0); bail: return status; diff --git a/fs/ocfs2/cluster/masklog.c b/fs/ocfs2/cluster/masklog.c index 23c732f27529..d8a0cb92cef6 100644 --- a/fs/ocfs2/cluster/masklog.c +++ b/fs/ocfs2/cluster/masklog.c @@ -109,6 +109,7 @@ static struct mlog_attribute mlog_attrs[MLOG_MAX_BITS] = { define_mask(CONN), define_mask(QUORUM), define_mask(EXPORT), + define_mask(XATTR), define_mask(ERROR), define_mask(NOTICE), define_mask(KTHREAD), diff --git a/fs/ocfs2/cluster/masklog.h b/fs/ocfs2/cluster/masklog.h index 597e064bb94f..57670c680471 100644 --- a/fs/ocfs2/cluster/masklog.h +++ b/fs/ocfs2/cluster/masklog.h @@ -112,6 +112,7 @@ #define ML_CONN 0x0000000004000000ULL /* net connection management */ #define ML_QUORUM 0x0000000008000000ULL /* net connection quorum */ #define ML_EXPORT 0x0000000010000000ULL /* ocfs2 export operations */ +#define ML_XATTR 0x0000000020000000ULL /* ocfs2 extended attributes */ /* bits that are infrequently given and frequently matched in the high word */ #define ML_ERROR 0x0000000100000000ULL /* sent to KERN_ERR */ #define ML_NOTICE 0x0000000200000000ULL /* setn to KERN_NOTICE */ diff --git a/fs/ocfs2/dir.c b/fs/ocfs2/dir.c index 9cce563fd627..026e6eb85187 100644 --- a/fs/ocfs2/dir.c +++ b/fs/ocfs2/dir.c @@ -82,6 +82,49 @@ static int ocfs2_do_extend_dir(struct super_block *sb, struct ocfs2_alloc_context *meta_ac, struct buffer_head **new_bh); +static struct buffer_head *ocfs2_bread(struct inode *inode, + int block, int *err, int reada) +{ + struct buffer_head *bh = NULL; + int tmperr; + u64 p_blkno; + int readflags = 0; + + if (reada) + readflags |= OCFS2_BH_READAHEAD; + + if (((u64)block << inode->i_sb->s_blocksize_bits) >= + i_size_read(inode)) { + BUG_ON(!reada); + return NULL; + } + + down_read(&OCFS2_I(inode)->ip_alloc_sem); + tmperr = ocfs2_extent_map_get_blocks(inode, block, &p_blkno, NULL, + NULL); + up_read(&OCFS2_I(inode)->ip_alloc_sem); + if (tmperr < 0) { + mlog_errno(tmperr); + goto fail; + } + + tmperr = ocfs2_read_blocks(inode, p_blkno, 1, &bh, readflags); + if (tmperr < 0) + goto fail; + + tmperr = 0; + + *err = 0; + return bh; + +fail: + brelse(bh); + bh = NULL; + + *err = -EIO; + return NULL; +} + /* * bh passed here can be an inode block or a dir data block, depending * on the inode inline data flag. @@ -188,8 +231,7 @@ static struct buffer_head *ocfs2_find_entry_id(const char *name, struct ocfs2_dinode *di; struct ocfs2_inline_data *data; - ret = ocfs2_read_block(OCFS2_SB(dir->i_sb), OCFS2_I(dir)->ip_blkno, - &di_bh, OCFS2_BH_CACHED, dir); + ret = ocfs2_read_block(dir, OCFS2_I(dir)->ip_blkno, &di_bh); if (ret) { mlog_errno(ret); goto out; @@ -260,14 +302,13 @@ restart: } if ((bh = bh_use[ra_ptr++]) == NULL) goto next; - wait_on_buffer(bh); - if (!buffer_uptodate(bh)) { - /* read error, skip block & hope for the best */ + if (ocfs2_read_block(dir, block, &bh)) { + /* read error, skip block & hope for the best. + * ocfs2_read_block() has released the bh. */ ocfs2_error(dir->i_sb, "reading directory %llu, " "offset %lu\n", (unsigned long long)OCFS2_I(dir)->ip_blkno, block); - brelse(bh); goto next; } i = ocfs2_search_dirblock(bh, dir, name, namelen, @@ -417,8 +458,7 @@ static inline int ocfs2_delete_entry_id(handle_t *handle, struct ocfs2_dinode *di; struct ocfs2_inline_data *data; - ret = ocfs2_read_block(OCFS2_SB(dir->i_sb), OCFS2_I(dir)->ip_blkno, - &di_bh, OCFS2_BH_CACHED, dir); + ret = ocfs2_read_block(dir, OCFS2_I(dir)->ip_blkno, &di_bh); if (ret) { mlog_errno(ret); goto out; @@ -596,8 +636,7 @@ static int ocfs2_dir_foreach_blk_id(struct inode *inode, struct ocfs2_inline_data *data; struct ocfs2_dir_entry *de; - ret = ocfs2_read_block(OCFS2_SB(inode->i_sb), OCFS2_I(inode)->ip_blkno, - &di_bh, OCFS2_BH_CACHED, inode); + ret = ocfs2_read_block(inode, OCFS2_I(inode)->ip_blkno, &di_bh); if (ret) { mlog(ML_ERROR, "Unable to read inode block for dir %llu\n", (unsigned long long)OCFS2_I(inode)->ip_blkno); @@ -716,8 +755,7 @@ static int ocfs2_dir_foreach_blk_el(struct inode *inode, for (i = ra_sectors >> (sb->s_blocksize_bits - 9); i > 0; i--) { tmp = ocfs2_bread(inode, ++blk, &err, 1); - if (tmp) - brelse(tmp); + brelse(tmp); } last_ra_blk = blk; ra_sectors = 8; @@ -899,10 +937,8 @@ int ocfs2_find_files_on_disk(const char *name, leave: if (status < 0) { *dirent = NULL; - if (*dirent_bh) { - brelse(*dirent_bh); - *dirent_bh = NULL; - } + brelse(*dirent_bh); + *dirent_bh = NULL; } mlog_exit(status); @@ -951,8 +987,7 @@ int ocfs2_check_dir_for_entry(struct inode *dir, ret = 0; bail: - if (dirent_bh) - brelse(dirent_bh); + brelse(dirent_bh); mlog_exit(ret); return ret; @@ -1127,8 +1162,7 @@ static int ocfs2_fill_new_dir_el(struct ocfs2_super *osb, status = 0; bail: - if (new_bh) - brelse(new_bh); + brelse(new_bh); mlog_exit(status); return status; @@ -1192,6 +1226,9 @@ static int ocfs2_expand_inline_dir(struct inode *dir, struct buffer_head *di_bh, struct buffer_head *dirdata_bh = NULL; struct ocfs2_dinode *di = (struct ocfs2_dinode *)di_bh->b_data; handle_t *handle; + struct ocfs2_extent_tree et; + + ocfs2_init_dinode_extent_tree(&et, dir, di_bh); alloc = ocfs2_clusters_for_bytes(sb, bytes); @@ -1305,8 +1342,8 @@ static int ocfs2_expand_inline_dir(struct inode *dir, struct buffer_head *di_bh, * This should never fail as our extent list is empty and all * related blocks have been journaled already. */ - ret = ocfs2_insert_extent(osb, handle, dir, di_bh, 0, blkno, len, 0, - NULL); + ret = ocfs2_insert_extent(osb, handle, dir, &et, 0, blkno, len, + 0, NULL); if (ret) { mlog_errno(ret); goto out_commit; @@ -1337,8 +1374,8 @@ static int ocfs2_expand_inline_dir(struct inode *dir, struct buffer_head *di_bh, } blkno = ocfs2_clusters_to_blocks(dir->i_sb, bit_off); - ret = ocfs2_insert_extent(osb, handle, dir, di_bh, 1, blkno, - len, 0, NULL); + ret = ocfs2_insert_extent(osb, handle, dir, &et, 1, + blkno, len, 0, NULL); if (ret) { mlog_errno(ret); goto out_commit; @@ -1383,9 +1420,9 @@ static int ocfs2_do_extend_dir(struct super_block *sb, if (extend) { u32 offset = OCFS2_I(dir)->ip_clusters; - status = ocfs2_do_extend_allocation(OCFS2_SB(sb), dir, &offset, - 1, 0, parent_fe_bh, handle, - data_ac, meta_ac, NULL); + status = ocfs2_add_inode_data(OCFS2_SB(sb), dir, &offset, + 1, 0, parent_fe_bh, handle, + data_ac, meta_ac, NULL); BUG_ON(status == -EAGAIN); if (status < 0) { mlog_errno(status); @@ -1430,12 +1467,14 @@ static int ocfs2_extend_dir(struct ocfs2_super *osb, int credits, num_free_extents, drop_alloc_sem = 0; loff_t dir_i_size; struct ocfs2_dinode *fe = (struct ocfs2_dinode *) parent_fe_bh->b_data; + struct ocfs2_extent_list *el = &fe->id2.i_list; struct ocfs2_alloc_context *data_ac = NULL; struct ocfs2_alloc_context *meta_ac = NULL; handle_t *handle = NULL; struct buffer_head *new_bh = NULL; struct ocfs2_dir_entry * de; struct super_block *sb = osb->sb; + struct ocfs2_extent_tree et; mlog_entry_void(); @@ -1479,7 +1518,8 @@ static int ocfs2_extend_dir(struct ocfs2_super *osb, spin_lock(&OCFS2_I(dir)->ip_lock); if (dir_i_size == ocfs2_clusters_to_bytes(sb, OCFS2_I(dir)->ip_clusters)) { spin_unlock(&OCFS2_I(dir)->ip_lock); - num_free_extents = ocfs2_num_free_extents(osb, dir, fe); + ocfs2_init_dinode_extent_tree(&et, dir, parent_fe_bh); + num_free_extents = ocfs2_num_free_extents(osb, dir, &et); if (num_free_extents < 0) { status = num_free_extents; mlog_errno(status); @@ -1487,7 +1527,7 @@ static int ocfs2_extend_dir(struct ocfs2_super *osb, } if (!num_free_extents) { - status = ocfs2_reserve_new_metadata(osb, fe, &meta_ac); + status = ocfs2_reserve_new_metadata(osb, el, &meta_ac); if (status < 0) { if (status != -ENOSPC) mlog_errno(status); @@ -1502,7 +1542,7 @@ static int ocfs2_extend_dir(struct ocfs2_super *osb, goto bail; } - credits = ocfs2_calc_extend_credits(sb, fe, 1); + credits = ocfs2_calc_extend_credits(sb, el, 1); } else { spin_unlock(&OCFS2_I(dir)->ip_lock); credits = OCFS2_SIMPLE_DIR_EXTEND_CREDITS; @@ -1568,8 +1608,7 @@ bail: if (meta_ac) ocfs2_free_alloc_context(meta_ac); - if (new_bh) - brelse(new_bh); + brelse(new_bh); mlog_exit(status); return status; @@ -1696,8 +1735,7 @@ static int ocfs2_find_dir_space_el(struct inode *dir, const char *name, status = 0; bail: - if (bh) - brelse(bh); + brelse(bh); mlog_exit(status); return status; @@ -1756,7 +1794,6 @@ int ocfs2_prepare_dir_for_insert(struct ocfs2_super *osb, *ret_de_bh = bh; bh = NULL; out: - if (bh) - brelse(bh); + brelse(bh); return ret; } diff --git a/fs/ocfs2/dlmglue.c b/fs/ocfs2/dlmglue.c index eae3d643a5e4..ec684426034b 100644 --- a/fs/ocfs2/dlmglue.c +++ b/fs/ocfs2/dlmglue.c @@ -2024,8 +2024,7 @@ static int ocfs2_inode_lock_update(struct inode *inode, } else { /* Boo, we have to go to disk. */ /* read bh, cast, ocfs2_refresh_inode */ - status = ocfs2_read_block(OCFS2_SB(inode->i_sb), oi->ip_blkno, - bh, OCFS2_BH_CACHED, inode); + status = ocfs2_read_block(inode, oi->ip_blkno, bh); if (status < 0) { mlog_errno(status); goto bail_refresh; @@ -2086,11 +2085,7 @@ static int ocfs2_assign_bh(struct inode *inode, return 0; } - status = ocfs2_read_block(OCFS2_SB(inode->i_sb), - OCFS2_I(inode)->ip_blkno, - ret_bh, - OCFS2_BH_CACHED, - inode); + status = ocfs2_read_block(inode, OCFS2_I(inode)->ip_blkno, ret_bh); if (status < 0) mlog_errno(status); diff --git a/fs/ocfs2/extent_map.c b/fs/ocfs2/extent_map.c index aed268e80b49..2baedac58234 100644 --- a/fs/ocfs2/extent_map.c +++ b/fs/ocfs2/extent_map.c @@ -293,8 +293,7 @@ static int ocfs2_last_eb_is_empty(struct inode *inode, struct ocfs2_extent_block *eb; struct ocfs2_extent_list *el; - ret = ocfs2_read_block(OCFS2_SB(inode->i_sb), last_eb_blk, - &eb_bh, OCFS2_BH_CACHED, inode); + ret = ocfs2_read_block(inode, last_eb_blk, &eb_bh); if (ret) { mlog_errno(ret); goto out; @@ -382,9 +381,9 @@ static int ocfs2_figure_hole_clusters(struct inode *inode, if (le64_to_cpu(eb->h_next_leaf_blk) == 0ULL) goto no_more_extents; - ret = ocfs2_read_block(OCFS2_SB(inode->i_sb), + ret = ocfs2_read_block(inode, le64_to_cpu(eb->h_next_leaf_blk), - &next_eb_bh, OCFS2_BH_CACHED, inode); + &next_eb_bh); if (ret) { mlog_errno(ret); goto out; @@ -551,6 +550,66 @@ static void ocfs2_relative_extent_offsets(struct super_block *sb, *num_clusters = le16_to_cpu(rec->e_leaf_clusters) - coff; } +int ocfs2_xattr_get_clusters(struct inode *inode, u32 v_cluster, + u32 *p_cluster, u32 *num_clusters, + struct ocfs2_extent_list *el) +{ + int ret = 0, i; + struct buffer_head *eb_bh = NULL; + struct ocfs2_extent_block *eb; + struct ocfs2_extent_rec *rec; + u32 coff; + + if (el->l_tree_depth) { + ret = ocfs2_find_leaf(inode, el, v_cluster, &eb_bh); + if (ret) { + mlog_errno(ret); + goto out; + } + + eb = (struct ocfs2_extent_block *) eb_bh->b_data; + el = &eb->h_list; + + if (el->l_tree_depth) { + ocfs2_error(inode->i_sb, + "Inode %lu has non zero tree depth in " + "xattr leaf block %llu\n", inode->i_ino, + (unsigned long long)eb_bh->b_blocknr); + ret = -EROFS; + goto out; + } + } + + i = ocfs2_search_extent_list(el, v_cluster); + if (i == -1) { + ret = -EROFS; + mlog_errno(ret); + goto out; + } else { + rec = &el->l_recs[i]; + BUG_ON(v_cluster < le32_to_cpu(rec->e_cpos)); + + if (!rec->e_blkno) { + ocfs2_error(inode->i_sb, "Inode %lu has bad extent " + "record (%u, %u, 0) in xattr", inode->i_ino, + le32_to_cpu(rec->e_cpos), + ocfs2_rec_clusters(el, rec)); + ret = -EROFS; + goto out; + } + coff = v_cluster - le32_to_cpu(rec->e_cpos); + *p_cluster = ocfs2_blocks_to_clusters(inode->i_sb, + le64_to_cpu(rec->e_blkno)); + *p_cluster = *p_cluster + coff; + if (num_clusters) + *num_clusters = ocfs2_rec_clusters(el, rec) - coff; + } +out: + if (eb_bh) + brelse(eb_bh); + return ret; +} + int ocfs2_get_clusters(struct inode *inode, u32 v_cluster, u32 *p_cluster, u32 *num_clusters, unsigned int *extent_flags) @@ -571,8 +630,7 @@ int ocfs2_get_clusters(struct inode *inode, u32 v_cluster, if (ret == 0) goto out; - ret = ocfs2_read_block(OCFS2_SB(inode->i_sb), OCFS2_I(inode)->ip_blkno, - &di_bh, OCFS2_BH_CACHED, inode); + ret = ocfs2_read_block(inode, OCFS2_I(inode)->ip_blkno, &di_bh); if (ret) { mlog_errno(ret); goto out; diff --git a/fs/ocfs2/extent_map.h b/fs/ocfs2/extent_map.h index 1b97490e1ea8..1c4aa8b06f34 100644 --- a/fs/ocfs2/extent_map.h +++ b/fs/ocfs2/extent_map.h @@ -53,4 +53,8 @@ int ocfs2_extent_map_get_blocks(struct inode *inode, u64 v_blkno, u64 *p_blkno, int ocfs2_fiemap(struct inode *inode, struct fiemap_extent_info *fieinfo, u64 map_start, u64 map_len); +int ocfs2_xattr_get_clusters(struct inode *inode, u32 v_cluster, + u32 *p_cluster, u32 *num_clusters, + struct ocfs2_extent_list *el); + #endif /* _EXTENT_MAP_H */ diff --git a/fs/ocfs2/file.c b/fs/ocfs2/file.c index ed38796052d2..8d3225a78073 100644 --- a/fs/ocfs2/file.c +++ b/fs/ocfs2/file.c @@ -55,6 +55,7 @@ #include "mmap.h" #include "suballoc.h" #include "super.h" +#include "xattr.h" #include "buffer_head_io.h" @@ -184,7 +185,7 @@ static int ocfs2_sync_file(struct file *file, goto bail; journal = osb->journal->j_journal; - err = journal_force_commit(journal); + err = jbd2_journal_force_commit(journal); bail: mlog_exit(err); @@ -488,7 +489,7 @@ bail: } /* - * extend allocation only here. + * extend file allocation only here. * we'll update all the disk stuff, and oip->alloc_size * * expect stuff to be locked, a transaction started and enough data / @@ -497,189 +498,25 @@ bail: * Will return -EAGAIN, and a reason if a restart is needed. * If passed in, *reason will always be set, even in error. */ -int ocfs2_do_extend_allocation(struct ocfs2_super *osb, - struct inode *inode, - u32 *logical_offset, - u32 clusters_to_add, - int mark_unwritten, - struct buffer_head *fe_bh, - handle_t *handle, - struct ocfs2_alloc_context *data_ac, - struct ocfs2_alloc_context *meta_ac, - enum ocfs2_alloc_restarted *reason_ret) +int ocfs2_add_inode_data(struct ocfs2_super *osb, + struct inode *inode, + u32 *logical_offset, + u32 clusters_to_add, + int mark_unwritten, + struct buffer_head *fe_bh, + handle_t *handle, + struct ocfs2_alloc_context *data_ac, + struct ocfs2_alloc_context *meta_ac, + enum ocfs2_alloc_restarted *reason_ret) { - int status = 0; - int free_extents; - struct ocfs2_dinode *fe = (struct ocfs2_dinode *) fe_bh->b_data; - enum ocfs2_alloc_restarted reason = RESTART_NONE; - u32 bit_off, num_bits; - u64 block; - u8 flags = 0; - - BUG_ON(!clusters_to_add); - - if (mark_unwritten) - flags = OCFS2_EXT_UNWRITTEN; - - free_extents = ocfs2_num_free_extents(osb, inode, fe); - if (free_extents < 0) { - status = free_extents; - mlog_errno(status); - goto leave; - } - - /* there are two cases which could cause us to EAGAIN in the - * we-need-more-metadata case: - * 1) we haven't reserved *any* - * 2) we are so fragmented, we've needed to add metadata too - * many times. */ - if (!free_extents && !meta_ac) { - mlog(0, "we haven't reserved any metadata!\n"); - status = -EAGAIN; - reason = RESTART_META; - goto leave; - } else if ((!free_extents) - && (ocfs2_alloc_context_bits_left(meta_ac) - < ocfs2_extend_meta_needed(fe))) { - mlog(0, "filesystem is really fragmented...\n"); - status = -EAGAIN; - reason = RESTART_META; - goto leave; - } - - status = __ocfs2_claim_clusters(osb, handle, data_ac, 1, - clusters_to_add, &bit_off, &num_bits); - if (status < 0) { - if (status != -ENOSPC) - mlog_errno(status); - goto leave; - } - - BUG_ON(num_bits > clusters_to_add); - - /* reserve our write early -- insert_extent may update the inode */ - status = ocfs2_journal_access(handle, inode, fe_bh, - OCFS2_JOURNAL_ACCESS_WRITE); - if (status < 0) { - mlog_errno(status); - goto leave; - } - - block = ocfs2_clusters_to_blocks(osb->sb, bit_off); - mlog(0, "Allocating %u clusters at block %u for inode %llu\n", - num_bits, bit_off, (unsigned long long)OCFS2_I(inode)->ip_blkno); - status = ocfs2_insert_extent(osb, handle, inode, fe_bh, - *logical_offset, block, num_bits, - flags, meta_ac); - if (status < 0) { - mlog_errno(status); - goto leave; - } - - status = ocfs2_journal_dirty(handle, fe_bh); - if (status < 0) { - mlog_errno(status); - goto leave; - } - - clusters_to_add -= num_bits; - *logical_offset += num_bits; - - if (clusters_to_add) { - mlog(0, "need to alloc once more, clusters = %u, wanted = " - "%u\n", fe->i_clusters, clusters_to_add); - status = -EAGAIN; - reason = RESTART_TRANS; - } - -leave: - mlog_exit(status); - if (reason_ret) - *reason_ret = reason; - return status; -} - -/* - * For a given allocation, determine which allocators will need to be - * accessed, and lock them, reserving the appropriate number of bits. - * - * Sparse file systems call this from ocfs2_write_begin_nolock() - * and ocfs2_allocate_unwritten_extents(). - * - * File systems which don't support holes call this from - * ocfs2_extend_allocation(). - */ -int ocfs2_lock_allocators(struct inode *inode, struct ocfs2_dinode *di, - u32 clusters_to_add, u32 extents_to_split, - struct ocfs2_alloc_context **data_ac, - struct ocfs2_alloc_context **meta_ac) -{ - int ret = 0, num_free_extents; - unsigned int max_recs_needed = clusters_to_add + 2 * extents_to_split; - struct ocfs2_super *osb = OCFS2_SB(inode->i_sb); - - *meta_ac = NULL; - if (data_ac) - *data_ac = NULL; - - BUG_ON(clusters_to_add != 0 && data_ac == NULL); - - mlog(0, "extend inode %llu, i_size = %lld, di->i_clusters = %u, " - "clusters_to_add = %u, extents_to_split = %u\n", - (unsigned long long)OCFS2_I(inode)->ip_blkno, (long long)i_size_read(inode), - le32_to_cpu(di->i_clusters), clusters_to_add, extents_to_split); - - num_free_extents = ocfs2_num_free_extents(osb, inode, di); - if (num_free_extents < 0) { - ret = num_free_extents; - mlog_errno(ret); - goto out; - } - - /* - * Sparse allocation file systems need to be more conservative - * with reserving room for expansion - the actual allocation - * happens while we've got a journal handle open so re-taking - * a cluster lock (because we ran out of room for another - * extent) will violate ordering rules. - * - * Most of the time we'll only be seeing this 1 cluster at a time - * anyway. - * - * Always lock for any unwritten extents - we might want to - * add blocks during a split. - */ - if (!num_free_extents || - (ocfs2_sparse_alloc(osb) && num_free_extents < max_recs_needed)) { - ret = ocfs2_reserve_new_metadata(osb, di, meta_ac); - if (ret < 0) { - if (ret != -ENOSPC) - mlog_errno(ret); - goto out; - } - } - - if (clusters_to_add == 0) - goto out; - - ret = ocfs2_reserve_clusters(osb, clusters_to_add, data_ac); - if (ret < 0) { - if (ret != -ENOSPC) - mlog_errno(ret); - goto out; - } - -out: - if (ret) { - if (*meta_ac) { - ocfs2_free_alloc_context(*meta_ac); - *meta_ac = NULL; - } + int ret; + struct ocfs2_extent_tree et; - /* - * We cannot have an error and a non null *data_ac. - */ - } + ocfs2_init_dinode_extent_tree(&et, inode, fe_bh); + ret = ocfs2_add_clusters_in_btree(osb, inode, logical_offset, + clusters_to_add, mark_unwritten, + &et, handle, + data_ac, meta_ac, reason_ret); return ret; } @@ -698,6 +535,7 @@ static int __ocfs2_extend_allocation(struct inode *inode, u32 logical_start, struct ocfs2_alloc_context *meta_ac = NULL; enum ocfs2_alloc_restarted why; struct ocfs2_super *osb = OCFS2_SB(inode->i_sb); + struct ocfs2_extent_tree et; mlog_entry("(clusters_to_add = %u)\n", clusters_to_add); @@ -707,8 +545,7 @@ static int __ocfs2_extend_allocation(struct inode *inode, u32 logical_start, */ BUG_ON(mark_unwritten && !ocfs2_sparse_alloc(osb)); - status = ocfs2_read_block(osb, OCFS2_I(inode)->ip_blkno, &bh, - OCFS2_BH_CACHED, inode); + status = ocfs2_read_block(inode, OCFS2_I(inode)->ip_blkno, &bh); if (status < 0) { mlog_errno(status); goto leave; @@ -724,14 +561,21 @@ static int __ocfs2_extend_allocation(struct inode *inode, u32 logical_start, restart_all: BUG_ON(le32_to_cpu(fe->i_clusters) != OCFS2_I(inode)->ip_clusters); - status = ocfs2_lock_allocators(inode, fe, clusters_to_add, 0, &data_ac, - &meta_ac); + mlog(0, "extend inode %llu, i_size = %lld, di->i_clusters = %u, " + "clusters_to_add = %u\n", + (unsigned long long)OCFS2_I(inode)->ip_blkno, + (long long)i_size_read(inode), le32_to_cpu(fe->i_clusters), + clusters_to_add); + ocfs2_init_dinode_extent_tree(&et, inode, bh); + status = ocfs2_lock_allocators(inode, &et, clusters_to_add, 0, + &data_ac, &meta_ac); if (status) { mlog_errno(status); goto leave; } - credits = ocfs2_calc_extend_credits(osb->sb, fe, clusters_to_add); + credits = ocfs2_calc_extend_credits(osb->sb, &fe->id2.i_list, + clusters_to_add); handle = ocfs2_start_trans(osb, credits); if (IS_ERR(handle)) { status = PTR_ERR(handle); @@ -753,16 +597,16 @@ restarted_transaction: prev_clusters = OCFS2_I(inode)->ip_clusters; - status = ocfs2_do_extend_allocation(osb, - inode, - &logical_start, - clusters_to_add, - mark_unwritten, - bh, - handle, - data_ac, - meta_ac, - &why); + status = ocfs2_add_inode_data(osb, + inode, + &logical_start, + clusters_to_add, + mark_unwritten, + bh, + handle, + data_ac, + meta_ac, + &why); if ((status < 0) && (status != -EAGAIN)) { if (status != -ENOSPC) mlog_errno(status); @@ -789,7 +633,7 @@ restarted_transaction: mlog(0, "restarting transaction.\n"); /* TODO: This can be more intelligent. */ credits = ocfs2_calc_extend_credits(osb->sb, - fe, + &fe->id2.i_list, clusters_to_add); status = ocfs2_extend_trans(handle, credits); if (status < 0) { @@ -826,10 +670,8 @@ leave: restart_func = 0; goto restart_all; } - if (bh) { - brelse(bh); - bh = NULL; - } + brelse(bh); + bh = NULL; mlog_exit(status); return status; @@ -1096,9 +938,15 @@ int ocfs2_setattr(struct dentry *dentry, struct iattr *attr) goto bail_unlock; } - if (i_size_read(inode) > attr->ia_size) + if (i_size_read(inode) > attr->ia_size) { + if (ocfs2_should_order_data(inode)) { + status = ocfs2_begin_ordered_truncate(inode, + attr->ia_size); + if (status) + goto bail_unlock; + } status = ocfs2_truncate_file(inode, bh, attr->ia_size); - else + } else status = ocfs2_extend_file(inode, bh, attr->ia_size); if (status < 0) { if (status != -ENOSPC) @@ -1140,8 +988,7 @@ bail_unlock_rw: if (size_change) ocfs2_rw_unlock(inode, 1); bail: - if (bh) - brelse(bh); + brelse(bh); mlog_exit(status); return status; @@ -1284,8 +1131,7 @@ static int ocfs2_write_remove_suid(struct inode *inode) struct buffer_head *bh = NULL; struct ocfs2_inode_info *oi = OCFS2_I(inode); - ret = ocfs2_read_block(OCFS2_SB(inode->i_sb), - oi->ip_blkno, &bh, OCFS2_BH_CACHED, inode); + ret = ocfs2_read_block(inode, oi->ip_blkno, &bh); if (ret < 0) { mlog_errno(ret); goto out; @@ -1311,9 +1157,8 @@ static int ocfs2_allocate_unwritten_extents(struct inode *inode, struct buffer_head *di_bh = NULL; if (OCFS2_I(inode)->ip_dyn_features & OCFS2_INLINE_DATA_FL) { - ret = ocfs2_read_block(OCFS2_SB(inode->i_sb), - OCFS2_I(inode)->ip_blkno, &di_bh, - OCFS2_BH_CACHED, inode); + ret = ocfs2_read_block(inode, OCFS2_I(inode)->ip_blkno, + &di_bh); if (ret) { mlog_errno(ret); goto out; @@ -1394,8 +1239,11 @@ static int __ocfs2_remove_inode_range(struct inode *inode, handle_t *handle; struct ocfs2_alloc_context *meta_ac = NULL; struct ocfs2_dinode *di = (struct ocfs2_dinode *)di_bh->b_data; + struct ocfs2_extent_tree et; - ret = ocfs2_lock_allocators(inode, di, 0, 1, NULL, &meta_ac); + ocfs2_init_dinode_extent_tree(&et, inode, di_bh); + + ret = ocfs2_lock_allocators(inode, &et, 0, 1, NULL, &meta_ac); if (ret) { mlog_errno(ret); return ret; @@ -1425,7 +1273,7 @@ static int __ocfs2_remove_inode_range(struct inode *inode, goto out; } - ret = ocfs2_remove_extent(inode, di_bh, cpos, len, handle, meta_ac, + ret = ocfs2_remove_extent(inode, &et, cpos, len, handle, meta_ac, dealloc); if (ret) { mlog_errno(ret); @@ -2040,7 +1888,7 @@ out_dio: */ if (old_size != i_size_read(inode) || old_clusters != OCFS2_I(inode)->ip_clusters) { - ret = journal_force_commit(osb->journal->j_journal); + ret = jbd2_journal_force_commit(osb->journal->j_journal); if (ret < 0) written = ret; } @@ -2227,6 +2075,10 @@ const struct inode_operations ocfs2_file_iops = { .setattr = ocfs2_setattr, .getattr = ocfs2_getattr, .permission = ocfs2_permission, + .setxattr = generic_setxattr, + .getxattr = generic_getxattr, + .listxattr = ocfs2_listxattr, + .removexattr = generic_removexattr, .fallocate = ocfs2_fallocate, .fiemap = ocfs2_fiemap, }; @@ -2237,6 +2089,10 @@ const struct inode_operations ocfs2_special_file_iops = { .permission = ocfs2_permission, }; +/* + * Other than ->lock, keep ocfs2_fops and ocfs2_dops in sync with + * ocfs2_fops_no_plocks and ocfs2_dops_no_plocks! + */ const struct file_operations ocfs2_fops = { .llseek = generic_file_llseek, .read = do_sync_read, @@ -2251,6 +2107,7 @@ const struct file_operations ocfs2_fops = { #ifdef CONFIG_COMPAT .compat_ioctl = ocfs2_compat_ioctl, #endif + .lock = ocfs2_lock, .flock = ocfs2_flock, .splice_read = ocfs2_file_splice_read, .splice_write = ocfs2_file_splice_write, @@ -2267,5 +2124,51 @@ const struct file_operations ocfs2_dops = { #ifdef CONFIG_COMPAT .compat_ioctl = ocfs2_compat_ioctl, #endif + .lock = ocfs2_lock, + .flock = ocfs2_flock, +}; + +/* + * POSIX-lockless variants of our file_operations. + * + * These will be used if the underlying cluster stack does not support + * posix file locking, if the user passes the "localflocks" mount + * option, or if we have a local-only fs. + * + * ocfs2_flock is in here because all stacks handle UNIX file locks, + * so we still want it in the case of no stack support for + * plocks. Internally, it will do the right thing when asked to ignore + * the cluster. + */ +const struct file_operations ocfs2_fops_no_plocks = { + .llseek = generic_file_llseek, + .read = do_sync_read, + .write = do_sync_write, + .mmap = ocfs2_mmap, + .fsync = ocfs2_sync_file, + .release = ocfs2_file_release, + .open = ocfs2_file_open, + .aio_read = ocfs2_file_aio_read, + .aio_write = ocfs2_file_aio_write, + .unlocked_ioctl = ocfs2_ioctl, +#ifdef CONFIG_COMPAT + .compat_ioctl = ocfs2_compat_ioctl, +#endif + .flock = ocfs2_flock, + .splice_read = ocfs2_file_splice_read, + .splice_write = ocfs2_file_splice_write, +}; + +const struct file_operations ocfs2_dops_no_plocks = { + .llseek = generic_file_llseek, + .read = generic_read_dir, + .readdir = ocfs2_readdir, + .fsync = ocfs2_sync_file, + .release = ocfs2_dir_release, + .open = ocfs2_dir_open, + .unlocked_ioctl = ocfs2_ioctl, +#ifdef CONFIG_COMPAT + .compat_ioctl = ocfs2_compat_ioctl, +#endif .flock = ocfs2_flock, }; diff --git a/fs/ocfs2/file.h b/fs/ocfs2/file.h index 1e27b4d017ea..e92382cbca5f 100644 --- a/fs/ocfs2/file.h +++ b/fs/ocfs2/file.h @@ -28,9 +28,12 @@ extern const struct file_operations ocfs2_fops; extern const struct file_operations ocfs2_dops; +extern const struct file_operations ocfs2_fops_no_plocks; +extern const struct file_operations ocfs2_dops_no_plocks; extern const struct inode_operations ocfs2_file_iops; extern const struct inode_operations ocfs2_special_file_iops; struct ocfs2_alloc_context; +enum ocfs2_alloc_restarted; struct ocfs2_file_private { struct file *fp_file; @@ -38,27 +41,18 @@ struct ocfs2_file_private { struct ocfs2_lock_res fp_flock; }; -enum ocfs2_alloc_restarted { - RESTART_NONE = 0, - RESTART_TRANS, - RESTART_META -}; -int ocfs2_do_extend_allocation(struct ocfs2_super *osb, - struct inode *inode, - u32 *logical_offset, - u32 clusters_to_add, - int mark_unwritten, - struct buffer_head *fe_bh, - handle_t *handle, - struct ocfs2_alloc_context *data_ac, - struct ocfs2_alloc_context *meta_ac, - enum ocfs2_alloc_restarted *reason_ret); +int ocfs2_add_inode_data(struct ocfs2_super *osb, + struct inode *inode, + u32 *logical_offset, + u32 clusters_to_add, + int mark_unwritten, + struct buffer_head *fe_bh, + handle_t *handle, + struct ocfs2_alloc_context *data_ac, + struct ocfs2_alloc_context *meta_ac, + enum ocfs2_alloc_restarted *reason_ret); int ocfs2_extend_no_holes(struct inode *inode, u64 new_i_size, u64 zero_to); -int ocfs2_lock_allocators(struct inode *inode, struct ocfs2_dinode *di, - u32 clusters_to_add, u32 extents_to_split, - struct ocfs2_alloc_context **data_ac, - struct ocfs2_alloc_context **meta_ac); int ocfs2_setattr(struct dentry *dentry, struct iattr *attr); int ocfs2_getattr(struct vfsmount *mnt, struct dentry *dentry, struct kstat *stat); diff --git a/fs/ocfs2/inode.c b/fs/ocfs2/inode.c index 7e9e4c79aec7..4903688f72a9 100644 --- a/fs/ocfs2/inode.c +++ b/fs/ocfs2/inode.c @@ -49,6 +49,7 @@ #include "symlink.h" #include "sysfile.h" #include "uptodate.h" +#include "xattr.h" #include "buffer_head_io.h" @@ -219,6 +220,7 @@ int ocfs2_populate_inode(struct inode *inode, struct ocfs2_dinode *fe, struct super_block *sb; struct ocfs2_super *osb; int status = -EINVAL; + int use_plocks = 1; mlog_entry("(0x%p, size:%llu)\n", inode, (unsigned long long)le64_to_cpu(fe->i_size)); @@ -226,6 +228,10 @@ int ocfs2_populate_inode(struct inode *inode, struct ocfs2_dinode *fe, sb = inode->i_sb; osb = OCFS2_SB(sb); + if ((osb->s_mount_opt & OCFS2_MOUNT_LOCALFLOCKS) || + ocfs2_mount_local(osb) || !ocfs2_stack_supports_plocks()) + use_plocks = 0; + /* this means that read_inode cannot create a superblock inode * today. change if needed. */ if (!OCFS2_IS_VALID_DINODE(fe) || @@ -295,13 +301,19 @@ int ocfs2_populate_inode(struct inode *inode, struct ocfs2_dinode *fe, switch (inode->i_mode & S_IFMT) { case S_IFREG: - inode->i_fop = &ocfs2_fops; + if (use_plocks) + inode->i_fop = &ocfs2_fops; + else + inode->i_fop = &ocfs2_fops_no_plocks; inode->i_op = &ocfs2_file_iops; i_size_write(inode, le64_to_cpu(fe->i_size)); break; case S_IFDIR: inode->i_op = &ocfs2_dir_iops; - inode->i_fop = &ocfs2_dops; + if (use_plocks) + inode->i_fop = &ocfs2_dops; + else + inode->i_fop = &ocfs2_dops_no_plocks; i_size_write(inode, le64_to_cpu(fe->i_size)); break; case S_IFLNK: @@ -448,8 +460,11 @@ static int ocfs2_read_locked_inode(struct inode *inode, } } - status = ocfs2_read_block(osb, args->fi_blkno, &bh, 0, - can_lock ? inode : NULL); + if (can_lock) + status = ocfs2_read_blocks(inode, args->fi_blkno, 1, &bh, + OCFS2_BH_IGNORE_CACHE); + else + status = ocfs2_read_blocks_sync(osb, args->fi_blkno, 1, &bh); if (status < 0) { mlog_errno(status); goto bail; @@ -522,6 +537,9 @@ static int ocfs2_truncate_for_delete(struct ocfs2_super *osb, * data and fast symlinks. */ if (fe->i_clusters) { + if (ocfs2_should_order_data(inode)) + ocfs2_begin_ordered_truncate(inode, 0); + handle = ocfs2_start_trans(osb, OCFS2_INODE_UPDATE_CREDITS); if (IS_ERR(handle)) { status = PTR_ERR(handle); @@ -730,6 +748,13 @@ static int ocfs2_wipe_inode(struct inode *inode, goto bail_unlock_dir; } + /*Free extended attribute resources associated with this inode.*/ + status = ocfs2_xattr_remove(inode, di_bh); + if (status < 0) { + mlog_errno(status); + goto bail_unlock_dir; + } + status = ocfs2_remove_inode(inode, di_bh, orphan_dir_inode, orphan_dir_bh); if (status < 0) @@ -1081,6 +1106,8 @@ void ocfs2_clear_inode(struct inode *inode) oi->ip_last_trans = 0; oi->ip_dir_start_lookup = 0; oi->ip_blkno = 0ULL; + jbd2_journal_release_jbd_inode(OCFS2_SB(inode->i_sb)->journal->j_journal, + &oi->ip_jinode); bail: mlog_exit_void(); @@ -1107,58 +1134,6 @@ void ocfs2_drop_inode(struct inode *inode) } /* - * TODO: this should probably be merged into ocfs2_get_block - * - * However, you now need to pay attention to the cont_prepare_write() - * stuff in ocfs2_get_block (that is, ocfs2_get_block pretty much - * expects never to extend). - */ -struct buffer_head *ocfs2_bread(struct inode *inode, - int block, int *err, int reada) -{ - struct buffer_head *bh = NULL; - int tmperr; - u64 p_blkno; - int readflags = OCFS2_BH_CACHED; - - if (reada) - readflags |= OCFS2_BH_READAHEAD; - - if (((u64)block << inode->i_sb->s_blocksize_bits) >= - i_size_read(inode)) { - BUG_ON(!reada); - return NULL; - } - - down_read(&OCFS2_I(inode)->ip_alloc_sem); - tmperr = ocfs2_extent_map_get_blocks(inode, block, &p_blkno, NULL, - NULL); - up_read(&OCFS2_I(inode)->ip_alloc_sem); - if (tmperr < 0) { - mlog_errno(tmperr); - goto fail; - } - - tmperr = ocfs2_read_block(OCFS2_SB(inode->i_sb), p_blkno, &bh, - readflags, inode); - if (tmperr < 0) - goto fail; - - tmperr = 0; - - *err = 0; - return bh; - -fail: - if (bh) { - brelse(bh); - bh = NULL; - } - *err = -EIO; - return NULL; -} - -/* * This is called from our getattr. */ int ocfs2_inode_revalidate(struct dentry *dentry) diff --git a/fs/ocfs2/inode.h b/fs/ocfs2/inode.h index 390a85596aa0..2f37af9bcc4a 100644 --- a/fs/ocfs2/inode.h +++ b/fs/ocfs2/inode.h @@ -40,6 +40,9 @@ struct ocfs2_inode_info /* protects allocation changes on this inode. */ struct rw_semaphore ip_alloc_sem; + /* protects extended attribute changes on this inode */ + struct rw_semaphore ip_xattr_sem; + /* These fields are protected by ip_lock */ spinlock_t ip_lock; u32 ip_open_count; @@ -68,6 +71,7 @@ struct ocfs2_inode_info struct ocfs2_extent_map ip_extent_map; struct inode vfs_inode; + struct jbd2_inode ip_jinode; }; /* @@ -113,8 +117,6 @@ extern struct kmem_cache *ocfs2_inode_cache; extern const struct address_space_operations ocfs2_aops; -struct buffer_head *ocfs2_bread(struct inode *inode, int block, - int *err, int reada); void ocfs2_clear_inode(struct inode *inode); void ocfs2_delete_inode(struct inode *inode); void ocfs2_drop_inode(struct inode *inode); diff --git a/fs/ocfs2/ioctl.c b/fs/ocfs2/ioctl.c index 7b142f0ce995..9fcd36dcc9a0 100644 --- a/fs/ocfs2/ioctl.c +++ b/fs/ocfs2/ioctl.c @@ -102,8 +102,7 @@ bail_unlock: bail: mutex_unlock(&inode->i_mutex); - if (bh) - brelse(bh); + brelse(bh); mlog_exit(status); return status; diff --git a/fs/ocfs2/journal.c b/fs/ocfs2/journal.c index c47bc2a809c2..81e40677eecb 100644 --- a/fs/ocfs2/journal.c +++ b/fs/ocfs2/journal.c @@ -215,9 +215,9 @@ static int ocfs2_commit_cache(struct ocfs2_super *osb) goto finally; } - journal_lock_updates(journal->j_journal); - status = journal_flush(journal->j_journal); - journal_unlock_updates(journal->j_journal); + jbd2_journal_lock_updates(journal->j_journal); + status = jbd2_journal_flush(journal->j_journal); + jbd2_journal_unlock_updates(journal->j_journal); if (status < 0) { up_write(&journal->j_trans_barrier); mlog_errno(status); @@ -264,7 +264,7 @@ handle_t *ocfs2_start_trans(struct ocfs2_super *osb, int max_buffs) down_read(&osb->journal->j_trans_barrier); - handle = journal_start(journal, max_buffs); + handle = jbd2_journal_start(journal, max_buffs); if (IS_ERR(handle)) { up_read(&osb->journal->j_trans_barrier); @@ -290,7 +290,7 @@ int ocfs2_commit_trans(struct ocfs2_super *osb, BUG_ON(!handle); - ret = journal_stop(handle); + ret = jbd2_journal_stop(handle); if (ret < 0) mlog_errno(ret); @@ -304,7 +304,7 @@ int ocfs2_commit_trans(struct ocfs2_super *osb, * transaction. extend_trans will either extend the current handle by * nblocks, or commit it and start a new one with nblocks credits. * - * This might call journal_restart() which will commit dirty buffers + * This might call jbd2_journal_restart() which will commit dirty buffers * and then restart the transaction. Before calling * ocfs2_extend_trans(), any changed blocks should have been * dirtied. After calling it, all blocks which need to be changed must @@ -332,7 +332,7 @@ int ocfs2_extend_trans(handle_t *handle, int nblocks) #ifdef CONFIG_OCFS2_DEBUG_FS status = 1; #else - status = journal_extend(handle, nblocks); + status = jbd2_journal_extend(handle, nblocks); if (status < 0) { mlog_errno(status); goto bail; @@ -340,8 +340,10 @@ int ocfs2_extend_trans(handle_t *handle, int nblocks) #endif if (status > 0) { - mlog(0, "journal_extend failed, trying journal_restart\n"); - status = journal_restart(handle, nblocks); + mlog(0, + "jbd2_journal_extend failed, trying " + "jbd2_journal_restart\n"); + status = jbd2_journal_restart(handle, nblocks); if (status < 0) { mlog_errno(status); goto bail; @@ -393,11 +395,11 @@ int ocfs2_journal_access(handle_t *handle, switch (type) { case OCFS2_JOURNAL_ACCESS_CREATE: case OCFS2_JOURNAL_ACCESS_WRITE: - status = journal_get_write_access(handle, bh); + status = jbd2_journal_get_write_access(handle, bh); break; case OCFS2_JOURNAL_ACCESS_UNDO: - status = journal_get_undo_access(handle, bh); + status = jbd2_journal_get_undo_access(handle, bh); break; default: @@ -422,7 +424,7 @@ int ocfs2_journal_dirty(handle_t *handle, mlog_entry("(bh->b_blocknr=%llu)\n", (unsigned long long)bh->b_blocknr); - status = journal_dirty_metadata(handle, bh); + status = jbd2_journal_dirty_metadata(handle, bh); if (status < 0) mlog(ML_ERROR, "Could not dirty metadata buffer. " "(bh->b_blocknr=%llu)\n", @@ -432,6 +434,7 @@ int ocfs2_journal_dirty(handle_t *handle, return status; } +#ifdef CONFIG_OCFS2_COMPAT_JBD int ocfs2_journal_dirty_data(handle_t *handle, struct buffer_head *bh) { @@ -443,8 +446,9 @@ int ocfs2_journal_dirty_data(handle_t *handle, return err; } +#endif -#define OCFS2_DEFAULT_COMMIT_INTERVAL (HZ * JBD_DEFAULT_MAX_COMMIT_AGE) +#define OCFS2_DEFAULT_COMMIT_INTERVAL (HZ * JBD2_DEFAULT_MAX_COMMIT_AGE) void ocfs2_set_journal_params(struct ocfs2_super *osb) { @@ -457,9 +461,9 @@ void ocfs2_set_journal_params(struct ocfs2_super *osb) spin_lock(&journal->j_state_lock); journal->j_commit_interval = commit_interval; if (osb->s_mount_opt & OCFS2_MOUNT_BARRIER) - journal->j_flags |= JFS_BARRIER; + journal->j_flags |= JBD2_BARRIER; else - journal->j_flags &= ~JFS_BARRIER; + journal->j_flags &= ~JBD2_BARRIER; spin_unlock(&journal->j_state_lock); } @@ -524,14 +528,14 @@ int ocfs2_journal_init(struct ocfs2_journal *journal, int *dirty) mlog(0, "inode->ip_clusters = %u\n", OCFS2_I(inode)->ip_clusters); /* call the kernels journal init function now */ - j_journal = journal_init_inode(inode); + j_journal = jbd2_journal_init_inode(inode); if (j_journal == NULL) { mlog(ML_ERROR, "Linux journal layer error\n"); status = -EINVAL; goto done; } - mlog(0, "Returned from journal_init_inode\n"); + mlog(0, "Returned from jbd2_journal_init_inode\n"); mlog(0, "j_journal->j_maxlen = %u\n", j_journal->j_maxlen); *dirty = (le32_to_cpu(di->id1.journal1.ij_flags) & @@ -550,8 +554,7 @@ done: if (status < 0) { if (inode_lock) ocfs2_inode_unlock(inode, 1); - if (bh != NULL) - brelse(bh); + brelse(bh); if (inode) { OCFS2_I(inode)->ip_open_count--; iput(inode); @@ -639,7 +642,7 @@ void ocfs2_journal_shutdown(struct ocfs2_super *osb) if (journal->j_state != OCFS2_JOURNAL_LOADED) goto done; - /* need to inc inode use count as journal_destroy will iput. */ + /* need to inc inode use count - jbd2_journal_destroy will iput. */ if (!igrab(inode)) BUG(); @@ -668,9 +671,9 @@ void ocfs2_journal_shutdown(struct ocfs2_super *osb) BUG_ON(atomic_read(&(osb->journal->j_num_trans)) != 0); if (ocfs2_mount_local(osb)) { - journal_lock_updates(journal->j_journal); - status = journal_flush(journal->j_journal); - journal_unlock_updates(journal->j_journal); + jbd2_journal_lock_updates(journal->j_journal); + status = jbd2_journal_flush(journal->j_journal); + jbd2_journal_unlock_updates(journal->j_journal); if (status < 0) mlog_errno(status); } @@ -686,7 +689,7 @@ void ocfs2_journal_shutdown(struct ocfs2_super *osb) } /* Shutdown the kernel journal system */ - journal_destroy(journal->j_journal); + jbd2_journal_destroy(journal->j_journal); OCFS2_I(inode)->ip_open_count--; @@ -711,15 +714,15 @@ static void ocfs2_clear_journal_error(struct super_block *sb, { int olderr; - olderr = journal_errno(journal); + olderr = jbd2_journal_errno(journal); if (olderr) { mlog(ML_ERROR, "File system error %d recorded in " "journal %u.\n", olderr, slot); mlog(ML_ERROR, "File system on device %s needs checking.\n", sb->s_id); - journal_ack_err(journal); - journal_clear_err(journal); + jbd2_journal_ack_err(journal); + jbd2_journal_clear_err(journal); } } @@ -734,7 +737,7 @@ int ocfs2_journal_load(struct ocfs2_journal *journal, int local, int replayed) osb = journal->j_osb; - status = journal_load(journal->j_journal); + status = jbd2_journal_load(journal->j_journal); if (status < 0) { mlog(ML_ERROR, "Failed to load journal!\n"); goto done; @@ -778,7 +781,7 @@ int ocfs2_journal_wipe(struct ocfs2_journal *journal, int full) BUG_ON(!journal); - status = journal_wipe(journal->j_journal, full); + status = jbd2_journal_wipe(journal->j_journal, full); if (status < 0) { mlog_errno(status); goto bail; @@ -847,9 +850,8 @@ static int ocfs2_force_read_journal(struct inode *inode) /* We are reading journal data which should not * be put in the uptodate cache */ - status = ocfs2_read_blocks(OCFS2_SB(inode->i_sb), - p_blkno, p_blocks, bhs, 0, - NULL); + status = ocfs2_read_blocks_sync(OCFS2_SB(inode->i_sb), + p_blkno, p_blocks, bhs); if (status < 0) { mlog_errno(status); goto bail; @@ -865,8 +867,7 @@ static int ocfs2_force_read_journal(struct inode *inode) bail: for(i = 0; i < CONCURRENT_JOURNAL_FILL; i++) - if (bhs[i]) - brelse(bhs[i]); + brelse(bhs[i]); mlog_exit(status); return status; } @@ -1133,7 +1134,8 @@ static int ocfs2_read_journal_inode(struct ocfs2_super *osb, } SET_INODE_JOURNAL(inode); - status = ocfs2_read_block(osb, OCFS2_I(inode)->ip_blkno, bh, 0, inode); + status = ocfs2_read_blocks(inode, OCFS2_I(inode)->ip_blkno, 1, bh, + OCFS2_BH_IGNORE_CACHE); if (status < 0) { mlog_errno(status); goto bail; @@ -1229,19 +1231,19 @@ static int ocfs2_replay_journal(struct ocfs2_super *osb, } mlog(0, "calling journal_init_inode\n"); - journal = journal_init_inode(inode); + journal = jbd2_journal_init_inode(inode); if (journal == NULL) { mlog(ML_ERROR, "Linux journal layer error\n"); status = -EIO; goto done; } - status = journal_load(journal); + status = jbd2_journal_load(journal); if (status < 0) { mlog_errno(status); if (!igrab(inode)) BUG(); - journal_destroy(journal); + jbd2_journal_destroy(journal); goto done; } @@ -1249,9 +1251,9 @@ static int ocfs2_replay_journal(struct ocfs2_super *osb, /* wipe the journal */ mlog(0, "flushing the journal.\n"); - journal_lock_updates(journal); - status = journal_flush(journal); - journal_unlock_updates(journal); + jbd2_journal_lock_updates(journal); + status = jbd2_journal_flush(journal); + jbd2_journal_unlock_updates(journal); if (status < 0) mlog_errno(status); @@ -1272,7 +1274,7 @@ static int ocfs2_replay_journal(struct ocfs2_super *osb, if (!igrab(inode)) BUG(); - journal_destroy(journal); + jbd2_journal_destroy(journal); done: /* drop the lock on this nodes journal */ @@ -1282,8 +1284,7 @@ done: if (inode) iput(inode); - if (bh) - brelse(bh); + brelse(bh); mlog_exit(status); return status; diff --git a/fs/ocfs2/journal.h b/fs/ocfs2/journal.h index 2178ebffa05f..d4d14e9a3cea 100644 --- a/fs/ocfs2/journal.h +++ b/fs/ocfs2/journal.h @@ -27,7 +27,12 @@ #define OCFS2_JOURNAL_H #include <linux/fs.h> -#include <linux/jbd.h> +#ifndef CONFIG_OCFS2_COMPAT_JBD +# include <linux/jbd2.h> +#else +# include <linux/jbd.h> +# include "ocfs2_jbd_compat.h" +#endif enum ocfs2_journal_state { OCFS2_JOURNAL_FREE = 0, @@ -215,8 +220,8 @@ static inline void ocfs2_checkpoint_inode(struct inode *inode) * buffer. Will have to call ocfs2_journal_dirty once * we've actually dirtied it. Type is one of . or . * ocfs2_journal_dirty - Mark a journalled buffer as having dirty data. - * ocfs2_journal_dirty_data - Indicate that a data buffer should go out before - * the current handle commits. + * ocfs2_jbd2_file_inode - Mark an inode so that its data goes out before + * the current handle commits. */ /* You must always start_trans with a number of buffs > 0, but it's @@ -268,8 +273,10 @@ int ocfs2_journal_access(handle_t *handle, */ int ocfs2_journal_dirty(handle_t *handle, struct buffer_head *bh); +#ifdef CONFIG_OCFS2_COMPAT_JBD int ocfs2_journal_dirty_data(handle_t *handle, struct buffer_head *bh); +#endif /* * Credit Macros: @@ -283,6 +290,9 @@ int ocfs2_journal_dirty_data(handle_t *handle, /* simple file updates like chmod, etc. */ #define OCFS2_INODE_UPDATE_CREDITS 1 +/* extended attribute block update */ +#define OCFS2_XATTR_BLOCK_UPDATE_CREDITS 1 + /* group extend. inode update and last group update. */ #define OCFS2_GROUP_EXTEND_CREDITS (OCFS2_INODE_UPDATE_CREDITS + 1) @@ -340,11 +350,23 @@ int ocfs2_journal_dirty_data(handle_t *handle, #define OCFS2_RENAME_CREDITS (3 * OCFS2_INODE_UPDATE_CREDITS + 3 \ + OCFS2_UNLINK_CREDITS) +/* global bitmap dinode, group desc., relinked group, + * suballocator dinode, group desc., relinked group, + * dinode, xattr block */ +#define OCFS2_XATTR_BLOCK_CREATE_CREDITS (OCFS2_SUBALLOC_ALLOC * 2 + \ + + OCFS2_INODE_UPDATE_CREDITS \ + + OCFS2_XATTR_BLOCK_UPDATE_CREDITS) + +/* + * Please note that the caller must make sure that root_el is the root + * of extent tree. So for an inode, it should be &fe->id2.i_list. Otherwise + * the result may be wrong. + */ static inline int ocfs2_calc_extend_credits(struct super_block *sb, - struct ocfs2_dinode *fe, + struct ocfs2_extent_list *root_el, u32 bits_wanted) { - int bitmap_blocks, sysfile_bitmap_blocks, dinode_blocks; + int bitmap_blocks, sysfile_bitmap_blocks, extent_blocks; /* bitmap dinode, group desc. + relinked group. */ bitmap_blocks = OCFS2_SUBALLOC_ALLOC; @@ -355,16 +377,16 @@ static inline int ocfs2_calc_extend_credits(struct super_block *sb, * however many metadata chunks needed * a remaining suballoc * alloc. */ sysfile_bitmap_blocks = 1 + - (OCFS2_SUBALLOC_ALLOC - 1) * ocfs2_extend_meta_needed(fe); + (OCFS2_SUBALLOC_ALLOC - 1) * ocfs2_extend_meta_needed(root_el); /* this does not include *new* metadata blocks, which are - * accounted for in sysfile_bitmap_blocks. fe + + * accounted for in sysfile_bitmap_blocks. root_el + * prev. last_eb_blk + blocks along edge of tree. * calc_symlink_credits passes because we just need 1 * credit for the dinode there. */ - dinode_blocks = 1 + 1 + le16_to_cpu(fe->id2.i_list.l_tree_depth); + extent_blocks = 1 + 1 + le16_to_cpu(root_el->l_tree_depth); - return bitmap_blocks + sysfile_bitmap_blocks + dinode_blocks; + return bitmap_blocks + sysfile_bitmap_blocks + extent_blocks; } static inline int ocfs2_calc_symlink_credits(struct super_block *sb) @@ -415,4 +437,16 @@ static inline int ocfs2_calc_tree_trunc_credits(struct super_block *sb, return credits; } +static inline int ocfs2_jbd2_file_inode(handle_t *handle, struct inode *inode) +{ + return jbd2_journal_file_inode(handle, &OCFS2_I(inode)->ip_jinode); +} + +static inline int ocfs2_begin_ordered_truncate(struct inode *inode, + loff_t new_size) +{ + return jbd2_journal_begin_ordered_truncate(&OCFS2_I(inode)->ip_jinode, + new_size); +} + #endif /* OCFS2_JOURNAL_H */ diff --git a/fs/ocfs2/localalloc.c b/fs/ocfs2/localalloc.c index 28e492e4ec88..687b28713c32 100644 --- a/fs/ocfs2/localalloc.c +++ b/fs/ocfs2/localalloc.c @@ -28,6 +28,7 @@ #include <linux/slab.h> #include <linux/highmem.h> #include <linux/bitops.h> +#include <linux/debugfs.h> #define MLOG_MASK_PREFIX ML_DISK_ALLOC #include <cluster/masklog.h> @@ -47,8 +48,6 @@ #define OCFS2_LOCAL_ALLOC(dinode) (&((dinode)->id2.i_lab)) -static inline int ocfs2_local_alloc_window_bits(struct ocfs2_super *osb); - static u32 ocfs2_local_alloc_count_bits(struct ocfs2_dinode *alloc); static int ocfs2_local_alloc_find_clear_bits(struct ocfs2_super *osb, @@ -75,24 +74,129 @@ static int ocfs2_local_alloc_new_window(struct ocfs2_super *osb, static int ocfs2_local_alloc_slide_window(struct ocfs2_super *osb, struct inode *local_alloc_inode); -static inline int ocfs2_local_alloc_window_bits(struct ocfs2_super *osb) +#ifdef CONFIG_OCFS2_FS_STATS + +static int ocfs2_la_debug_open(struct inode *inode, struct file *file) +{ + file->private_data = inode->i_private; + return 0; +} + +#define LA_DEBUG_BUF_SZ PAGE_CACHE_SIZE +#define LA_DEBUG_VER 1 +static ssize_t ocfs2_la_debug_read(struct file *file, char __user *userbuf, + size_t count, loff_t *ppos) +{ + static DEFINE_MUTEX(la_debug_mutex); + struct ocfs2_super *osb = file->private_data; + int written, ret; + char *buf = osb->local_alloc_debug_buf; + + mutex_lock(&la_debug_mutex); + memset(buf, 0, LA_DEBUG_BUF_SZ); + + written = snprintf(buf, LA_DEBUG_BUF_SZ, + "0x%x\t0x%llx\t%u\t%u\t0x%x\n", + LA_DEBUG_VER, + (unsigned long long)osb->la_last_gd, + osb->local_alloc_default_bits, + osb->local_alloc_bits, osb->local_alloc_state); + + ret = simple_read_from_buffer(userbuf, count, ppos, buf, written); + + mutex_unlock(&la_debug_mutex); + return ret; +} + +static const struct file_operations ocfs2_la_debug_fops = { + .open = ocfs2_la_debug_open, + .read = ocfs2_la_debug_read, +}; + +static void ocfs2_init_la_debug(struct ocfs2_super *osb) +{ + osb->local_alloc_debug_buf = kmalloc(LA_DEBUG_BUF_SZ, GFP_NOFS); + if (!osb->local_alloc_debug_buf) + return; + + osb->local_alloc_debug = debugfs_create_file("local_alloc_stats", + S_IFREG|S_IRUSR, + osb->osb_debug_root, + osb, + &ocfs2_la_debug_fops); + if (!osb->local_alloc_debug) { + kfree(osb->local_alloc_debug_buf); + osb->local_alloc_debug_buf = NULL; + } +} + +static void ocfs2_shutdown_la_debug(struct ocfs2_super *osb) +{ + if (osb->local_alloc_debug) + debugfs_remove(osb->local_alloc_debug); + + if (osb->local_alloc_debug_buf) + kfree(osb->local_alloc_debug_buf); + + osb->local_alloc_debug_buf = NULL; + osb->local_alloc_debug = NULL; +} +#else /* CONFIG_OCFS2_FS_STATS */ +static void ocfs2_init_la_debug(struct ocfs2_super *osb) +{ + return; +} +static void ocfs2_shutdown_la_debug(struct ocfs2_super *osb) +{ + return; +} +#endif + +static inline int ocfs2_la_state_enabled(struct ocfs2_super *osb) { - BUG_ON(osb->s_clustersize_bits > 20); + return (osb->local_alloc_state == OCFS2_LA_THROTTLED || + osb->local_alloc_state == OCFS2_LA_ENABLED); +} - /* Size local alloc windows by the megabyte */ - return osb->local_alloc_size << (20 - osb->s_clustersize_bits); +void ocfs2_local_alloc_seen_free_bits(struct ocfs2_super *osb, + unsigned int num_clusters) +{ + spin_lock(&osb->osb_lock); + if (osb->local_alloc_state == OCFS2_LA_DISABLED || + osb->local_alloc_state == OCFS2_LA_THROTTLED) + if (num_clusters >= osb->local_alloc_default_bits) { + cancel_delayed_work(&osb->la_enable_wq); + osb->local_alloc_state = OCFS2_LA_ENABLED; + } + spin_unlock(&osb->osb_lock); +} + +void ocfs2_la_enable_worker(struct work_struct *work) +{ + struct ocfs2_super *osb = + container_of(work, struct ocfs2_super, + la_enable_wq.work); + spin_lock(&osb->osb_lock); + osb->local_alloc_state = OCFS2_LA_ENABLED; + spin_unlock(&osb->osb_lock); } /* * Tell us whether a given allocation should use the local alloc * file. Otherwise, it has to go to the main bitmap. + * + * This function does semi-dirty reads of local alloc size and state! + * This is ok however, as the values are re-checked once under mutex. */ int ocfs2_alloc_should_use_local(struct ocfs2_super *osb, u64 bits) { - int la_bits = ocfs2_local_alloc_window_bits(osb); int ret = 0; + int la_bits; + + spin_lock(&osb->osb_lock); + la_bits = osb->local_alloc_bits; - if (osb->local_alloc_state != OCFS2_LA_ENABLED) + if (!ocfs2_la_state_enabled(osb)) goto bail; /* la_bits should be at least twice the size (in clusters) of @@ -106,6 +210,7 @@ int ocfs2_alloc_should_use_local(struct ocfs2_super *osb, u64 bits) bail: mlog(0, "state=%d, bits=%llu, la_bits=%d, ret=%d\n", osb->local_alloc_state, (unsigned long long)bits, la_bits, ret); + spin_unlock(&osb->osb_lock); return ret; } @@ -120,14 +225,18 @@ int ocfs2_load_local_alloc(struct ocfs2_super *osb) mlog_entry_void(); - if (osb->local_alloc_size == 0) + ocfs2_init_la_debug(osb); + + if (osb->local_alloc_bits == 0) goto bail; - if (ocfs2_local_alloc_window_bits(osb) >= osb->bitmap_cpg) { + if (osb->local_alloc_bits >= osb->bitmap_cpg) { mlog(ML_NOTICE, "Requested local alloc window %d is larger " "than max possible %u. Using defaults.\n", - ocfs2_local_alloc_window_bits(osb), (osb->bitmap_cpg - 1)); - osb->local_alloc_size = OCFS2_DEFAULT_LOCAL_ALLOC_SIZE; + osb->local_alloc_bits, (osb->bitmap_cpg - 1)); + osb->local_alloc_bits = + ocfs2_megabytes_to_clusters(osb->sb, + OCFS2_DEFAULT_LOCAL_ALLOC_SIZE); } /* read the alloc off disk */ @@ -139,8 +248,8 @@ int ocfs2_load_local_alloc(struct ocfs2_super *osb) goto bail; } - status = ocfs2_read_block(osb, OCFS2_I(inode)->ip_blkno, - &alloc_bh, 0, inode); + status = ocfs2_read_blocks(inode, OCFS2_I(inode)->ip_blkno, 1, + &alloc_bh, OCFS2_BH_IGNORE_CACHE); if (status < 0) { mlog_errno(status); goto bail; @@ -185,13 +294,14 @@ int ocfs2_load_local_alloc(struct ocfs2_super *osb) bail: if (status < 0) - if (alloc_bh) - brelse(alloc_bh); + brelse(alloc_bh); if (inode) iput(inode); - mlog(0, "Local alloc window bits = %d\n", - ocfs2_local_alloc_window_bits(osb)); + if (status < 0) + ocfs2_shutdown_la_debug(osb); + + mlog(0, "Local alloc window bits = %d\n", osb->local_alloc_bits); mlog_exit(status); return status; @@ -217,6 +327,11 @@ void ocfs2_shutdown_local_alloc(struct ocfs2_super *osb) mlog_entry_void(); + cancel_delayed_work(&osb->la_enable_wq); + flush_workqueue(ocfs2_wq); + + ocfs2_shutdown_la_debug(osb); + if (osb->local_alloc_state == OCFS2_LA_UNUSED) goto out; @@ -295,8 +410,7 @@ out_commit: ocfs2_commit_trans(osb, handle); out_unlock: - if (main_bm_bh) - brelse(main_bm_bh); + brelse(main_bm_bh); ocfs2_inode_unlock(main_bm_inode, 1); @@ -345,8 +459,8 @@ int ocfs2_begin_local_alloc_recovery(struct ocfs2_super *osb, mutex_lock(&inode->i_mutex); - status = ocfs2_read_block(osb, OCFS2_I(inode)->ip_blkno, - &alloc_bh, 0, inode); + status = ocfs2_read_blocks(inode, OCFS2_I(inode)->ip_blkno, 1, + &alloc_bh, OCFS2_BH_IGNORE_CACHE); if (status < 0) { mlog_errno(status); goto bail; @@ -372,8 +486,7 @@ bail: *alloc_copy = NULL; } - if (alloc_bh) - brelse(alloc_bh); + brelse(alloc_bh); if (inode) { mutex_unlock(&inode->i_mutex); @@ -441,8 +554,7 @@ out_unlock: out_mutex: mutex_unlock(&main_bm_inode->i_mutex); - if (main_bm_bh) - brelse(main_bm_bh); + brelse(main_bm_bh); iput(main_bm_inode); @@ -453,8 +565,48 @@ out: return status; } +/* Check to see if the local alloc window is within ac->ac_max_block */ +static int ocfs2_local_alloc_in_range(struct inode *inode, + struct ocfs2_alloc_context *ac, + u32 bits_wanted) +{ + struct ocfs2_super *osb = OCFS2_SB(inode->i_sb); + struct ocfs2_dinode *alloc; + struct ocfs2_local_alloc *la; + int start; + u64 block_off; + + if (!ac->ac_max_block) + return 1; + + alloc = (struct ocfs2_dinode *) osb->local_alloc_bh->b_data; + la = OCFS2_LOCAL_ALLOC(alloc); + + start = ocfs2_local_alloc_find_clear_bits(osb, alloc, bits_wanted); + if (start == -1) { + mlog_errno(-ENOSPC); + return 0; + } + + /* + * Converting (bm_off + start + bits_wanted) to blocks gives us + * the blkno just past our actual allocation. This is perfect + * to compare with ac_max_block. + */ + block_off = ocfs2_clusters_to_blocks(inode->i_sb, + le32_to_cpu(la->la_bm_off) + + start + bits_wanted); + mlog(0, "Checking %llu against %llu\n", + (unsigned long long)block_off, + (unsigned long long)ac->ac_max_block); + if (block_off > ac->ac_max_block) + return 0; + + return 1; +} + /* - * make sure we've got at least bitswanted contiguous bits in the + * make sure we've got at least bits_wanted contiguous bits in the * local alloc. You lose them when you drop i_mutex. * * We will add ourselves to the transaction passed in, but may start @@ -485,16 +637,18 @@ int ocfs2_reserve_local_alloc_bits(struct ocfs2_super *osb, mutex_lock(&local_alloc_inode->i_mutex); - if (osb->local_alloc_state != OCFS2_LA_ENABLED) { - status = -ENOSPC; - goto bail; - } - - if (bits_wanted > ocfs2_local_alloc_window_bits(osb)) { - mlog(0, "Asking for more than my max window size!\n"); + /* + * We must double check state and allocator bits because + * another process may have changed them while holding i_mutex. + */ + spin_lock(&osb->osb_lock); + if (!ocfs2_la_state_enabled(osb) || + (bits_wanted > osb->local_alloc_bits)) { + spin_unlock(&osb->osb_lock); status = -ENOSPC; goto bail; } + spin_unlock(&osb->osb_lock); alloc = (struct ocfs2_dinode *) osb->local_alloc_bh->b_data; @@ -522,6 +676,36 @@ int ocfs2_reserve_local_alloc_bits(struct ocfs2_super *osb, mlog_errno(status); goto bail; } + + /* + * Under certain conditions, the window slide code + * might have reduced the number of bits available or + * disabled the the local alloc entirely. Re-check + * here and return -ENOSPC if necessary. + */ + status = -ENOSPC; + if (!ocfs2_la_state_enabled(osb)) + goto bail; + + free_bits = le32_to_cpu(alloc->id1.bitmap1.i_total) - + le32_to_cpu(alloc->id1.bitmap1.i_used); + if (bits_wanted > free_bits) + goto bail; + } + + if (ac->ac_max_block) + mlog(0, "Calling in_range for max block %llu\n", + (unsigned long long)ac->ac_max_block); + + if (!ocfs2_local_alloc_in_range(local_alloc_inode, ac, + bits_wanted)) { + /* + * The window is outside ac->ac_max_block. + * This errno tells the caller to keep localalloc enabled + * but to get the allocation from the main bitmap. + */ + status = -EFBIG; + goto bail; } ac->ac_inode = local_alloc_inode; @@ -789,6 +973,85 @@ bail: return status; } +enum ocfs2_la_event { + OCFS2_LA_EVENT_SLIDE, /* Normal window slide. */ + OCFS2_LA_EVENT_FRAGMENTED, /* The global bitmap has + * enough bits theoretically + * free, but a contiguous + * allocation could not be + * found. */ + OCFS2_LA_EVENT_ENOSPC, /* Global bitmap doesn't have + * enough bits free to satisfy + * our request. */ +}; +#define OCFS2_LA_ENABLE_INTERVAL (30 * HZ) +/* + * Given an event, calculate the size of our next local alloc window. + * + * This should always be called under i_mutex of the local alloc inode + * so that local alloc disabling doesn't race with processes trying to + * use the allocator. + * + * Returns the state which the local alloc was left in. This value can + * be ignored by some paths. + */ +static int ocfs2_recalc_la_window(struct ocfs2_super *osb, + enum ocfs2_la_event event) +{ + unsigned int bits; + int state; + + spin_lock(&osb->osb_lock); + if (osb->local_alloc_state == OCFS2_LA_DISABLED) { + WARN_ON_ONCE(osb->local_alloc_state == OCFS2_LA_DISABLED); + goto out_unlock; + } + + /* + * ENOSPC and fragmentation are treated similarly for now. + */ + if (event == OCFS2_LA_EVENT_ENOSPC || + event == OCFS2_LA_EVENT_FRAGMENTED) { + /* + * We ran out of contiguous space in the primary + * bitmap. Drastically reduce the number of bits used + * by local alloc until we have to disable it. + */ + bits = osb->local_alloc_bits >> 1; + if (bits > ocfs2_megabytes_to_clusters(osb->sb, 1)) { + /* + * By setting state to THROTTLED, we'll keep + * the number of local alloc bits used down + * until an event occurs which would give us + * reason to assume the bitmap situation might + * have changed. + */ + osb->local_alloc_state = OCFS2_LA_THROTTLED; + osb->local_alloc_bits = bits; + } else { + osb->local_alloc_state = OCFS2_LA_DISABLED; + } + queue_delayed_work(ocfs2_wq, &osb->la_enable_wq, + OCFS2_LA_ENABLE_INTERVAL); + goto out_unlock; + } + + /* + * Don't increase the size of the local alloc window until we + * know we might be able to fulfill the request. Otherwise, we + * risk bouncing around the global bitmap during periods of + * low space. + */ + if (osb->local_alloc_state != OCFS2_LA_THROTTLED) + osb->local_alloc_bits = osb->local_alloc_default_bits; + +out_unlock: + state = osb->local_alloc_state; + spin_unlock(&osb->osb_lock); + + return state; +} + static int ocfs2_local_alloc_reserve_for_window(struct ocfs2_super *osb, struct ocfs2_alloc_context **ac, struct inode **bitmap_inode, @@ -803,12 +1066,21 @@ static int ocfs2_local_alloc_reserve_for_window(struct ocfs2_super *osb, goto bail; } - (*ac)->ac_bits_wanted = ocfs2_local_alloc_window_bits(osb); +retry_enospc: + (*ac)->ac_bits_wanted = osb->local_alloc_bits; status = ocfs2_reserve_cluster_bitmap_bits(osb, *ac); + if (status == -ENOSPC) { + if (ocfs2_recalc_la_window(osb, OCFS2_LA_EVENT_ENOSPC) == + OCFS2_LA_DISABLED) + goto bail; + + ocfs2_free_ac_resource(*ac); + memset(*ac, 0, sizeof(struct ocfs2_alloc_context)); + goto retry_enospc; + } if (status < 0) { - if (status != -ENOSPC) - mlog_errno(status); + mlog_errno(status); goto bail; } @@ -849,7 +1121,7 @@ static int ocfs2_local_alloc_new_window(struct ocfs2_super *osb, "one\n"); mlog(0, "Allocating %u clusters for a new window.\n", - ocfs2_local_alloc_window_bits(osb)); + osb->local_alloc_bits); /* Instruct the allocation code to try the most recently used * cluster group. We'll re-record the group used this pass @@ -859,9 +1131,36 @@ static int ocfs2_local_alloc_new_window(struct ocfs2_super *osb, /* we used the generic suballoc reserve function, but we set * everything up nicely, so there's no reason why we can't use * the more specific cluster api to claim bits. */ - status = ocfs2_claim_clusters(osb, handle, ac, - ocfs2_local_alloc_window_bits(osb), + status = ocfs2_claim_clusters(osb, handle, ac, osb->local_alloc_bits, &cluster_off, &cluster_count); + if (status == -ENOSPC) { +retry_enospc: + /* + * Note: We could also try syncing the journal here to + * allow use of any free bits which the current + * transaction can't give us access to. --Mark + */ + if (ocfs2_recalc_la_window(osb, OCFS2_LA_EVENT_FRAGMENTED) == + OCFS2_LA_DISABLED) + goto bail; + + status = ocfs2_claim_clusters(osb, handle, ac, + osb->local_alloc_bits, + &cluster_off, + &cluster_count); + if (status == -ENOSPC) + goto retry_enospc; + /* + * We only shrunk the *minimum* number of in our + * request - it's entirely possible that the allocator + * might give us more than we asked for. + */ + if (status == 0) { + spin_lock(&osb->osb_lock); + osb->local_alloc_bits = cluster_count; + spin_unlock(&osb->osb_lock); + } + } if (status < 0) { if (status != -ENOSPC) mlog_errno(status); @@ -905,6 +1204,8 @@ static int ocfs2_local_alloc_slide_window(struct ocfs2_super *osb, mlog_entry_void(); + ocfs2_recalc_la_window(osb, OCFS2_LA_EVENT_SLIDE); + /* This will lock the main bitmap for us. */ status = ocfs2_local_alloc_reserve_for_window(osb, &ac, @@ -976,8 +1277,7 @@ bail: if (handle) ocfs2_commit_trans(osb, handle); - if (main_bm_bh) - brelse(main_bm_bh); + brelse(main_bm_bh); if (main_bm_inode) iput(main_bm_inode); diff --git a/fs/ocfs2/localalloc.h b/fs/ocfs2/localalloc.h index 3f76631e110c..ac5ea9f86653 100644 --- a/fs/ocfs2/localalloc.h +++ b/fs/ocfs2/localalloc.h @@ -52,4 +52,8 @@ int ocfs2_claim_local_alloc_bits(struct ocfs2_super *osb, u32 *bit_off, u32 *num_bits); +void ocfs2_local_alloc_seen_free_bits(struct ocfs2_super *osb, + unsigned int num_clusters); +void ocfs2_la_enable_worker(struct work_struct *work); + #endif /* OCFS2_LOCALALLOC_H */ diff --git a/fs/ocfs2/locks.c b/fs/ocfs2/locks.c index 203f87143877..544ac6245175 100644 --- a/fs/ocfs2/locks.c +++ b/fs/ocfs2/locks.c @@ -24,6 +24,7 @@ */ #include <linux/fs.h> +#include <linux/fcntl.h> #define MLOG_MASK_PREFIX ML_INODE #include <cluster/masklog.h> @@ -32,6 +33,7 @@ #include "dlmglue.h" #include "file.h" +#include "inode.h" #include "locks.h" static int ocfs2_do_flock(struct file *file, struct inode *inode, @@ -123,3 +125,16 @@ int ocfs2_flock(struct file *file, int cmd, struct file_lock *fl) else return ocfs2_do_flock(file, inode, cmd, fl); } + +int ocfs2_lock(struct file *file, int cmd, struct file_lock *fl) +{ + struct inode *inode = file->f_mapping->host; + struct ocfs2_super *osb = OCFS2_SB(inode->i_sb); + + if (!(fl->fl_flags & FL_POSIX)) + return -ENOLCK; + if (__mandatory_lock(inode)) + return -ENOLCK; + + return ocfs2_plock(osb->cconn, OCFS2_I(inode)->ip_blkno, file, cmd, fl); +} diff --git a/fs/ocfs2/locks.h b/fs/ocfs2/locks.h index 9743ef2324ec..496d488b271f 100644 --- a/fs/ocfs2/locks.h +++ b/fs/ocfs2/locks.h @@ -27,5 +27,6 @@ #define OCFS2_LOCKS_H int ocfs2_flock(struct file *file, int cmd, struct file_lock *fl); +int ocfs2_lock(struct file *file, int cmd, struct file_lock *fl); #endif /* OCFS2_LOCKS_H */ diff --git a/fs/ocfs2/namei.c b/fs/ocfs2/namei.c index d5d808fe0140..485a6aa0ad39 100644 --- a/fs/ocfs2/namei.c +++ b/fs/ocfs2/namei.c @@ -60,6 +60,7 @@ #include "symlink.h" #include "sysfile.h" #include "uptodate.h" +#include "xattr.h" #include "buffer_head_io.h" @@ -327,14 +328,9 @@ leave: if (status == -ENOSPC) mlog(0, "Disk is full\n"); - if (new_fe_bh) - brelse(new_fe_bh); - - if (de_bh) - brelse(de_bh); - - if (parent_fe_bh) - brelse(parent_fe_bh); + brelse(new_fe_bh); + brelse(de_bh); + brelse(parent_fe_bh); if ((status < 0) && inode) iput(inode); @@ -647,12 +643,9 @@ out_unlock_inode: out: ocfs2_inode_unlock(dir, 1); - if (de_bh) - brelse(de_bh); - if (fe_bh) - brelse(fe_bh); - if (parent_fe_bh) - brelse(parent_fe_bh); + brelse(de_bh); + brelse(fe_bh); + brelse(parent_fe_bh); mlog_exit(err); @@ -851,17 +844,10 @@ leave: iput(orphan_dir); } - if (fe_bh) - brelse(fe_bh); - - if (dirent_bh) - brelse(dirent_bh); - - if (parent_node_bh) - brelse(parent_node_bh); - - if (orphan_entry_bh) - brelse(orphan_entry_bh); + brelse(fe_bh); + brelse(dirent_bh); + brelse(parent_node_bh); + brelse(orphan_entry_bh); mlog_exit(status); @@ -1372,24 +1358,15 @@ bail: if (new_inode) iput(new_inode); - if (newfe_bh) - brelse(newfe_bh); - if (old_inode_bh) - brelse(old_inode_bh); - if (old_dir_bh) - brelse(old_dir_bh); - if (new_dir_bh) - brelse(new_dir_bh); - if (new_de_bh) - brelse(new_de_bh); - if (old_de_bh) - brelse(old_de_bh); - if (old_inode_de_bh) - brelse(old_inode_de_bh); - if (orphan_entry_bh) - brelse(orphan_entry_bh); - if (insert_entry_bh) - brelse(insert_entry_bh); + brelse(newfe_bh); + brelse(old_inode_bh); + brelse(old_dir_bh); + brelse(new_dir_bh); + brelse(new_de_bh); + brelse(old_de_bh); + brelse(old_inode_de_bh); + brelse(orphan_entry_bh); + brelse(insert_entry_bh); mlog_exit(status); @@ -1492,8 +1469,7 @@ bail: if (bhs) { for(i = 0; i < blocks; i++) - if (bhs[i]) - brelse(bhs[i]); + brelse(bhs[i]); kfree(bhs); } @@ -1598,10 +1574,10 @@ static int ocfs2_symlink(struct inode *dir, u32 offset = 0; inode->i_op = &ocfs2_symlink_inode_operations; - status = ocfs2_do_extend_allocation(osb, inode, &offset, 1, 0, - new_fe_bh, - handle, data_ac, NULL, - NULL); + status = ocfs2_add_inode_data(osb, inode, &offset, 1, 0, + new_fe_bh, + handle, data_ac, NULL, + NULL); if (status < 0) { if (status != -ENOSPC && status != -EINTR) { mlog(ML_ERROR, @@ -1659,12 +1635,9 @@ bail: ocfs2_inode_unlock(dir, 1); - if (new_fe_bh) - brelse(new_fe_bh); - if (parent_fe_bh) - brelse(parent_fe_bh); - if (de_bh) - brelse(de_bh); + brelse(new_fe_bh); + brelse(parent_fe_bh); + brelse(de_bh); if (inode_ac) ocfs2_free_alloc_context(inode_ac); if (data_ac) @@ -1759,8 +1732,7 @@ leave: iput(orphan_dir_inode); } - if (orphan_dir_bh) - brelse(orphan_dir_bh); + brelse(orphan_dir_bh); mlog_exit(status); return status; @@ -1780,10 +1752,9 @@ static int ocfs2_orphan_add(struct ocfs2_super *osb, mlog_entry("(inode->i_ino = %lu)\n", inode->i_ino); - status = ocfs2_read_block(osb, + status = ocfs2_read_block(orphan_dir_inode, OCFS2_I(orphan_dir_inode)->ip_blkno, - &orphan_dir_bh, OCFS2_BH_CACHED, - orphan_dir_inode); + &orphan_dir_bh); if (status < 0) { mlog_errno(status); goto leave; @@ -1829,8 +1800,7 @@ static int ocfs2_orphan_add(struct ocfs2_super *osb, (unsigned long long)OCFS2_I(inode)->ip_blkno, osb->slot_num); leave: - if (orphan_dir_bh) - brelse(orphan_dir_bh); + brelse(orphan_dir_bh); mlog_exit(status); return status; @@ -1898,8 +1868,7 @@ int ocfs2_orphan_del(struct ocfs2_super *osb, } leave: - if (target_de_bh) - brelse(target_de_bh); + brelse(target_de_bh); mlog_exit(status); return status; @@ -1918,4 +1887,8 @@ const struct inode_operations ocfs2_dir_iops = { .setattr = ocfs2_setattr, .getattr = ocfs2_getattr, .permission = ocfs2_permission, + .setxattr = generic_setxattr, + .getxattr = generic_getxattr, + .listxattr = ocfs2_listxattr, + .removexattr = generic_removexattr, }; diff --git a/fs/ocfs2/ocfs2.h b/fs/ocfs2/ocfs2.h index 7f625f2b1117..a21a465490c4 100644 --- a/fs/ocfs2/ocfs2.h +++ b/fs/ocfs2/ocfs2.h @@ -34,7 +34,12 @@ #include <linux/workqueue.h> #include <linux/kref.h> #include <linux/mutex.h> -#include <linux/jbd.h> +#ifndef CONFIG_OCFS2_COMPAT_JBD +# include <linux/jbd2.h> +#else +# include <linux/jbd.h> +# include "ocfs2_jbd_compat.h" +#endif /* For union ocfs2_dlm_lksb */ #include "stackglue.h" @@ -171,9 +176,13 @@ struct ocfs2_alloc_stats enum ocfs2_local_alloc_state { - OCFS2_LA_UNUSED = 0, - OCFS2_LA_ENABLED, - OCFS2_LA_DISABLED + OCFS2_LA_UNUSED = 0, /* Local alloc will never be used for + * this mountpoint. */ + OCFS2_LA_ENABLED, /* Local alloc is in use. */ + OCFS2_LA_THROTTLED, /* Local alloc is in use, but number + * of bits has been reduced. */ + OCFS2_LA_DISABLED /* Local alloc has temporarily been + * disabled. */ }; enum ocfs2_mount_options @@ -184,6 +193,8 @@ enum ocfs2_mount_options OCFS2_MOUNT_ERRORS_PANIC = 1 << 3, /* Panic on errors */ OCFS2_MOUNT_DATA_WRITEBACK = 1 << 4, /* No data ordering */ OCFS2_MOUNT_LOCALFLOCKS = 1 << 5, /* No cluster aware user file locks */ + OCFS2_MOUNT_NOUSERXATTR = 1 << 6, /* No user xattr */ + OCFS2_MOUNT_INODE64 = 1 << 7, /* Allow inode numbers > 2^32 */ }; #define OCFS2_OSB_SOFT_RO 0x0001 @@ -214,6 +225,7 @@ struct ocfs2_super u32 bitmap_cpg; u8 *uuid; char *uuid_str; + u32 uuid_hash; u8 *vol_label; u64 first_cluster_group_blkno; u32 fs_generation; @@ -241,6 +253,7 @@ struct ocfs2_super int s_sectsize_bits; int s_clustersize; int s_clustersize_bits; + unsigned int s_xattr_inline_size; atomic_t vol_state; struct mutex recovery_lock; @@ -252,11 +265,27 @@ struct ocfs2_super struct ocfs2_journal *journal; unsigned long osb_commit_interval; - int local_alloc_size; - enum ocfs2_local_alloc_state local_alloc_state; + struct delayed_work la_enable_wq; + + /* + * Must hold local alloc i_mutex and osb->osb_lock to change + * local_alloc_bits. Reads can be done under either lock. + */ + unsigned int local_alloc_bits; + unsigned int local_alloc_default_bits; + + enum ocfs2_local_alloc_state local_alloc_state; /* protected + * by osb_lock */ + struct buffer_head *local_alloc_bh; + u64 la_last_gd; +#ifdef CONFIG_OCFS2_FS_STATS + struct dentry *local_alloc_debug; + char *local_alloc_debug_buf; +#endif + /* Next two fields are for local node slot recovery during * mount. */ int dirty; @@ -340,6 +369,13 @@ static inline int ocfs2_supports_inline_data(struct ocfs2_super *osb) return 0; } +static inline int ocfs2_supports_xattr(struct ocfs2_super *osb) +{ + if (osb->s_feature_incompat & OCFS2_FEATURE_INCOMPAT_XATTR) + return 1; + return 0; +} + /* set / clear functions because cluster events can make these happen * in parallel so we want the transitions to be atomic. this also * means that any future flags osb_flags must be protected by spinlock @@ -554,6 +590,14 @@ static inline unsigned int ocfs2_pages_per_cluster(struct super_block *sb) return pages_per_cluster; } +static inline unsigned int ocfs2_megabytes_to_clusters(struct super_block *sb, + unsigned int megs) +{ + BUILD_BUG_ON(OCFS2_MAX_CLUSTERSIZE > 1048576); + + return megs << (20 - OCFS2_SB(sb)->s_clustersize_bits); +} + static inline void ocfs2_init_inode_steal_slot(struct ocfs2_super *osb) { spin_lock(&osb->osb_lock); diff --git a/fs/ocfs2/ocfs2_fs.h b/fs/ocfs2/ocfs2_fs.h index 4f619850ccf7..f24ce3d3f956 100644 --- a/fs/ocfs2/ocfs2_fs.h +++ b/fs/ocfs2/ocfs2_fs.h @@ -64,6 +64,7 @@ #define OCFS2_INODE_SIGNATURE "INODE01" #define OCFS2_EXTENT_BLOCK_SIGNATURE "EXBLK01" #define OCFS2_GROUP_DESC_SIGNATURE "GROUP01" +#define OCFS2_XATTR_BLOCK_SIGNATURE "XATTR01" /* Compatibility flags */ #define OCFS2_HAS_COMPAT_FEATURE(sb,mask) \ @@ -90,7 +91,8 @@ | OCFS2_FEATURE_INCOMPAT_SPARSE_ALLOC \ | OCFS2_FEATURE_INCOMPAT_INLINE_DATA \ | OCFS2_FEATURE_INCOMPAT_EXTENDED_SLOT_MAP \ - | OCFS2_FEATURE_INCOMPAT_USERSPACE_STACK) + | OCFS2_FEATURE_INCOMPAT_USERSPACE_STACK \ + | OCFS2_FEATURE_INCOMPAT_XATTR) #define OCFS2_FEATURE_RO_COMPAT_SUPP OCFS2_FEATURE_RO_COMPAT_UNWRITTEN /* @@ -127,10 +129,6 @@ /* Support for data packed into inode blocks */ #define OCFS2_FEATURE_INCOMPAT_INLINE_DATA 0x0040 -/* Support for the extended slot map */ -#define OCFS2_FEATURE_INCOMPAT_EXTENDED_SLOT_MAP 0x100 - - /* * Support for alternate, userspace cluster stacks. If set, the superblock * field s_cluster_info contains a tag for the alternate stack in use as @@ -142,6 +140,12 @@ */ #define OCFS2_FEATURE_INCOMPAT_USERSPACE_STACK 0x0080 +/* Support for the extended slot map */ +#define OCFS2_FEATURE_INCOMPAT_EXTENDED_SLOT_MAP 0x100 + +/* Support for extended attributes */ +#define OCFS2_FEATURE_INCOMPAT_XATTR 0x0200 + /* * backup superblock flag is used to indicate that this volume * has backup superblocks. @@ -299,6 +303,12 @@ struct ocfs2_new_group_input { */ #define OCFS2_DEFAULT_LOCAL_ALLOC_SIZE 8 +/* + * Inline extended attribute size (in bytes) + * The value chosen should be aligned to 16 byte boundaries. + */ +#define OCFS2_MIN_XATTR_INLINE_SIZE 256 + struct ocfs2_system_inode_info { char *si_name; int si_iflags; @@ -563,7 +573,7 @@ struct ocfs2_super_block { /*40*/ __le16 s_max_slots; /* Max number of simultaneous mounts before tunefs required */ __le16 s_tunefs_flag; - __le32 s_reserved1; + __le32 s_uuid_hash; /* hash value of uuid */ __le64 s_first_cluster_group; /* Block offset of 1st cluster * group header */ /*50*/ __u8 s_label[OCFS2_MAX_VOL_LABEL_LEN]; /* Label for mounting, etc. */ @@ -571,7 +581,11 @@ struct ocfs2_super_block { /*A0*/ struct ocfs2_cluster_info s_cluster_info; /* Selected userspace stack. Only valid with INCOMPAT flag. */ -/*B8*/ __le64 s_reserved2[17]; /* Fill out superblock */ +/*B8*/ __le16 s_xattr_inline_size; /* extended attribute inline size + for this fs*/ + __le16 s_reserved0; + __le32 s_reserved1; +/*C0*/ __le64 s_reserved2[16]; /* Fill out superblock */ /*140*/ /* @@ -621,7 +635,8 @@ struct ocfs2_dinode { belongs to */ __le16 i_suballoc_bit; /* Bit offset in suballocator block group */ -/*10*/ __le32 i_reserved0; +/*10*/ __le16 i_reserved0; + __le16 i_xattr_inline_size; __le32 i_clusters; /* Cluster count */ __le32 i_uid; /* Owner UID */ __le32 i_gid; /* Owning GID */ @@ -640,11 +655,12 @@ struct ocfs2_dinode { __le32 i_atime_nsec; __le32 i_ctime_nsec; __le32 i_mtime_nsec; - __le32 i_attr; +/*70*/ __le32 i_attr; __le16 i_orphaned_slot; /* Only valid when OCFS2_ORPHANED_FL was set in i_flags */ __le16 i_dyn_features; -/*70*/ __le64 i_reserved2[8]; + __le64 i_xattr_loc; +/*80*/ __le64 i_reserved2[7]; /*B8*/ union { __le64 i_pad1; /* Generic way to refer to this 64bit union */ @@ -715,6 +731,136 @@ struct ocfs2_group_desc /*40*/ __u8 bg_bitmap[0]; }; +/* + * On disk extended attribute structure for OCFS2. + */ + +/* + * ocfs2_xattr_entry indicates one extend attribute. + * + * Note that it can be stored in inode, one block or one xattr bucket. + */ +struct ocfs2_xattr_entry { + __le32 xe_name_hash; /* hash value of xattr prefix+suffix. */ + __le16 xe_name_offset; /* byte offset from the 1st etnry in the local + local xattr storage(inode, xattr block or + xattr bucket). */ + __u8 xe_name_len; /* xattr name len, does't include prefix. */ + __u8 xe_type; /* the low 7 bits indicates the name prefix's + * type and the highest 1 bits indicate whether + * the EA is stored in the local storage. */ + __le64 xe_value_size; /* real xattr value length. */ +}; + +/* + * On disk structure for xattr header. + * + * One ocfs2_xattr_header describes how many ocfs2_xattr_entry records in + * the local xattr storage. + */ +struct ocfs2_xattr_header { + __le16 xh_count; /* contains the count of how + many records are in the + local xattr storage. */ + __le16 xh_free_start; /* current offset for storing + xattr. */ + __le16 xh_name_value_len; /* total length of name/value + length in this bucket. */ + __le16 xh_num_buckets; /* bucket nums in one extent + record, only valid in the + first bucket. */ + __le64 xh_csum; + struct ocfs2_xattr_entry xh_entries[0]; /* xattr entry list. */ +}; + +/* + * On disk structure for xattr value root. + * + * It is used when one extended attribute's size is larger, and we will save it + * in an outside cluster. It will stored in a b-tree like file content. + */ +struct ocfs2_xattr_value_root { +/*00*/ __le32 xr_clusters; /* clusters covered by xattr value. */ + __le32 xr_reserved0; + __le64 xr_last_eb_blk; /* Pointer to last extent block */ +/*10*/ struct ocfs2_extent_list xr_list; /* Extent record list */ +}; + +/* + * On disk structure for xattr tree root. + * + * It is used when there are too many extended attributes for one file. These + * attributes will be organized and stored in an indexed-btree. + */ +struct ocfs2_xattr_tree_root { +/*00*/ __le32 xt_clusters; /* clusters covered by xattr. */ + __le32 xt_reserved0; + __le64 xt_last_eb_blk; /* Pointer to last extent block */ +/*10*/ struct ocfs2_extent_list xt_list; /* Extent record list */ +}; + +#define OCFS2_XATTR_INDEXED 0x1 +#define OCFS2_HASH_SHIFT 5 +#define OCFS2_XATTR_ROUND 3 +#define OCFS2_XATTR_SIZE(size) (((size) + OCFS2_XATTR_ROUND) & \ + ~(OCFS2_XATTR_ROUND)) + +#define OCFS2_XATTR_BUCKET_SIZE 4096 +#define OCFS2_XATTR_MAX_BLOCKS_PER_BUCKET (OCFS2_XATTR_BUCKET_SIZE \ + / OCFS2_MIN_BLOCKSIZE) + +/* + * On disk structure for xattr block. + */ +struct ocfs2_xattr_block { +/*00*/ __u8 xb_signature[8]; /* Signature for verification */ + __le16 xb_suballoc_slot; /* Slot suballocator this + block belongs to. */ + __le16 xb_suballoc_bit; /* Bit offset in suballocator + block group */ + __le32 xb_fs_generation; /* Must match super block */ +/*10*/ __le64 xb_blkno; /* Offset on disk, in blocks */ + __le64 xb_csum; +/*20*/ __le16 xb_flags; /* Indicates whether this block contains + real xattr or a xattr tree. */ + __le16 xb_reserved0; + __le32 xb_reserved1; + __le64 xb_reserved2; +/*30*/ union { + struct ocfs2_xattr_header xb_header; /* xattr header if this + block contains xattr */ + struct ocfs2_xattr_tree_root xb_root;/* xattr tree root if this + block cotains xattr + tree. */ + } xb_attrs; +}; + +#define OCFS2_XATTR_ENTRY_LOCAL 0x80 +#define OCFS2_XATTR_TYPE_MASK 0x7F +static inline void ocfs2_xattr_set_local(struct ocfs2_xattr_entry *xe, + int local) +{ + if (local) + xe->xe_type |= OCFS2_XATTR_ENTRY_LOCAL; + else + xe->xe_type &= ~OCFS2_XATTR_ENTRY_LOCAL; +} + +static inline int ocfs2_xattr_is_local(struct ocfs2_xattr_entry *xe) +{ + return xe->xe_type & OCFS2_XATTR_ENTRY_LOCAL; +} + +static inline void ocfs2_xattr_set_type(struct ocfs2_xattr_entry *xe, int type) +{ + xe->xe_type |= type & OCFS2_XATTR_TYPE_MASK; +} + +static inline int ocfs2_xattr_get_type(struct ocfs2_xattr_entry *xe) +{ + return xe->xe_type & OCFS2_XATTR_TYPE_MASK; +} + #ifdef __KERNEL__ static inline int ocfs2_fast_symlink_chars(struct super_block *sb) { @@ -728,6 +874,20 @@ static inline int ocfs2_max_inline_data(struct super_block *sb) offsetof(struct ocfs2_dinode, id2.i_data.id_data); } +static inline int ocfs2_max_inline_data_with_xattr(struct super_block *sb, + struct ocfs2_dinode *di) +{ + unsigned int xattrsize = le16_to_cpu(di->i_xattr_inline_size); + + if (le16_to_cpu(di->i_dyn_features) & OCFS2_INLINE_XATTR_FL) + return sb->s_blocksize - + offsetof(struct ocfs2_dinode, id2.i_data.id_data) - + xattrsize; + else + return sb->s_blocksize - + offsetof(struct ocfs2_dinode, id2.i_data.id_data); +} + static inline int ocfs2_extent_recs_per_inode(struct super_block *sb) { int size; @@ -738,6 +898,24 @@ static inline int ocfs2_extent_recs_per_inode(struct super_block *sb) return size / sizeof(struct ocfs2_extent_rec); } +static inline int ocfs2_extent_recs_per_inode_with_xattr( + struct super_block *sb, + struct ocfs2_dinode *di) +{ + int size; + unsigned int xattrsize = le16_to_cpu(di->i_xattr_inline_size); + + if (le16_to_cpu(di->i_dyn_features) & OCFS2_INLINE_XATTR_FL) + size = sb->s_blocksize - + offsetof(struct ocfs2_dinode, id2.i_list.l_recs) - + xattrsize; + else + size = sb->s_blocksize - + offsetof(struct ocfs2_dinode, id2.i_list.l_recs); + + return size / sizeof(struct ocfs2_extent_rec); +} + static inline int ocfs2_chain_recs_per_inode(struct super_block *sb) { int size; @@ -801,6 +979,17 @@ static inline u64 ocfs2_backup_super_blkno(struct super_block *sb, int index) return 0; } + +static inline u16 ocfs2_xattr_recs_per_xb(struct super_block *sb) +{ + int size; + + size = sb->s_blocksize - + offsetof(struct ocfs2_xattr_block, + xb_attrs.xb_root.xt_list.l_recs); + + return size / sizeof(struct ocfs2_extent_rec); +} #else static inline int ocfs2_fast_symlink_chars(int blocksize) { @@ -884,6 +1073,17 @@ static inline uint64_t ocfs2_backup_super_blkno(int blocksize, int index) return 0; } + +static inline int ocfs2_xattr_recs_per_xb(int blocksize) +{ + int size; + + size = blocksize - + offsetof(struct ocfs2_xattr_block, + xb_attrs.xb_root.xt_list.l_recs); + + return size / sizeof(struct ocfs2_extent_rec); +} #endif /* __KERNEL__ */ diff --git a/fs/ocfs2/ocfs2_jbd_compat.h b/fs/ocfs2/ocfs2_jbd_compat.h new file mode 100644 index 000000000000..b91c78f8f558 --- /dev/null +++ b/fs/ocfs2/ocfs2_jbd_compat.h @@ -0,0 +1,82 @@ +/* -*- mode: c; c-basic-offset: 8; -*- + * vim: noexpandtab sw=8 ts=8 sts=0: + * + * ocfs2_jbd_compat.h + * + * Compatibility defines for JBD. + * + * Copyright (C) 2008 Oracle. All rights reserved. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + */ + +#ifndef OCFS2_JBD_COMPAT_H +#define OCFS2_JBD_COMPAT_H + +#ifndef CONFIG_OCFS2_COMPAT_JBD +# error Should not have been included +#endif + +struct jbd2_inode { + unsigned int dummy; +}; + +#define JBD2_BARRIER JFS_BARRIER +#define JBD2_DEFAULT_MAX_COMMIT_AGE JBD_DEFAULT_MAX_COMMIT_AGE + +#define jbd2_journal_ack_err journal_ack_err +#define jbd2_journal_clear_err journal_clear_err +#define jbd2_journal_destroy journal_destroy +#define jbd2_journal_dirty_metadata journal_dirty_metadata +#define jbd2_journal_errno journal_errno +#define jbd2_journal_extend journal_extend +#define jbd2_journal_flush journal_flush +#define jbd2_journal_force_commit journal_force_commit +#define jbd2_journal_get_write_access journal_get_write_access +#define jbd2_journal_get_undo_access journal_get_undo_access +#define jbd2_journal_init_inode journal_init_inode +#define jbd2_journal_invalidatepage journal_invalidatepage +#define jbd2_journal_load journal_load +#define jbd2_journal_lock_updates journal_lock_updates +#define jbd2_journal_restart journal_restart +#define jbd2_journal_start journal_start +#define jbd2_journal_start_commit journal_start_commit +#define jbd2_journal_stop journal_stop +#define jbd2_journal_try_to_free_buffers journal_try_to_free_buffers +#define jbd2_journal_unlock_updates journal_unlock_updates +#define jbd2_journal_wipe journal_wipe +#define jbd2_log_wait_commit log_wait_commit + +static inline int jbd2_journal_file_inode(handle_t *handle, + struct jbd2_inode *inode) +{ + return 0; +} + +static inline int jbd2_journal_begin_ordered_truncate(struct jbd2_inode *inode, + loff_t new_size) +{ + return 0; +} + +static inline void jbd2_journal_init_jbd_inode(struct jbd2_inode *jinode, + struct inode *inode) +{ + return; +} + +static inline void jbd2_journal_release_jbd_inode(journal_t *journal, + struct jbd2_inode *jinode) +{ + return; +} + + +#endif /* OCFS2_JBD_COMPAT_H */ diff --git a/fs/ocfs2/resize.c b/fs/ocfs2/resize.c index 8166968e9015..ffd48db229a7 100644 --- a/fs/ocfs2/resize.c +++ b/fs/ocfs2/resize.c @@ -200,7 +200,7 @@ static int update_backups(struct inode * inode, u32 clusters, char *data) if (cluster > clusters) break; - ret = ocfs2_read_block(osb, blkno, &backup, 0, NULL); + ret = ocfs2_read_blocks_sync(osb, blkno, 1, &backup); if (ret < 0) { mlog_errno(ret); break; @@ -236,8 +236,8 @@ static void ocfs2_update_super_and_backups(struct inode *inode, * update the superblock last. * It doesn't matter if the write failed. */ - ret = ocfs2_read_block(osb, OCFS2_SUPER_BLOCK_BLKNO, - &super_bh, 0, NULL); + ret = ocfs2_read_blocks_sync(osb, OCFS2_SUPER_BLOCK_BLKNO, 1, + &super_bh); if (ret < 0) { mlog_errno(ret); goto out; @@ -332,8 +332,7 @@ int ocfs2_group_extend(struct inode * inode, int new_clusters) lgd_blkno = ocfs2_which_cluster_group(main_bm_inode, first_new_cluster - 1); - ret = ocfs2_read_block(osb, lgd_blkno, &group_bh, OCFS2_BH_CACHED, - main_bm_inode); + ret = ocfs2_read_block(main_bm_inode, lgd_blkno, &group_bh); if (ret < 0) { mlog_errno(ret); goto out_unlock; @@ -540,7 +539,7 @@ int ocfs2_group_add(struct inode *inode, struct ocfs2_new_group_input *input) goto out_unlock; } - ret = ocfs2_read_block(osb, input->group, &group_bh, 0, NULL); + ret = ocfs2_read_blocks_sync(osb, input->group, 1, &group_bh); if (ret < 0) { mlog(ML_ERROR, "Can't read the group descriptor # %llu " "from the device.", (unsigned long long)input->group); diff --git a/fs/ocfs2/slot_map.c b/fs/ocfs2/slot_map.c index bb5ff8939bf1..bdda2d8f8508 100644 --- a/fs/ocfs2/slot_map.c +++ b/fs/ocfs2/slot_map.c @@ -150,8 +150,8 @@ int ocfs2_refresh_slot_info(struct ocfs2_super *osb) * be !NULL. Thus, ocfs2_read_blocks() will ignore blocknr. If * this is not true, the read of -1 (UINT64_MAX) will fail. */ - ret = ocfs2_read_blocks(osb, -1, si->si_blocks, si->si_bh, 0, - si->si_inode); + ret = ocfs2_read_blocks(si->si_inode, -1, si->si_blocks, si->si_bh, + OCFS2_BH_IGNORE_CACHE); if (ret == 0) { spin_lock(&osb->osb_lock); ocfs2_update_slot_info(si); @@ -404,7 +404,8 @@ static int ocfs2_map_slot_buffers(struct ocfs2_super *osb, (unsigned long long)blkno); bh = NULL; /* Acquire a fresh bh */ - status = ocfs2_read_block(osb, blkno, &bh, 0, si->si_inode); + status = ocfs2_read_blocks(si->si_inode, blkno, 1, &bh, + OCFS2_BH_IGNORE_CACHE); if (status < 0) { mlog_errno(status); goto bail; diff --git a/fs/ocfs2/stack_user.c b/fs/ocfs2/stack_user.c index 353fc35c6748..faec2d879357 100644 --- a/fs/ocfs2/stack_user.c +++ b/fs/ocfs2/stack_user.c @@ -28,6 +28,7 @@ #include "ocfs2.h" /* For struct ocfs2_lock_res */ #include "stackglue.h" +#include <linux/dlm_plock.h> /* * The control protocol starts with a handshake. Until the handshake @@ -746,6 +747,37 @@ static void user_dlm_dump_lksb(union ocfs2_dlm_lksb *lksb) { } +static int user_plock(struct ocfs2_cluster_connection *conn, + u64 ino, + struct file *file, + int cmd, + struct file_lock *fl) +{ + /* + * This more or less just demuxes the plock request into any + * one of three dlm calls. + * + * Internally, fs/dlm will pass these to a misc device, which + * a userspace daemon will read and write to. + * + * For now, cancel requests (which happen internally only), + * are turned into unlocks. Most of this function taken from + * gfs2_lock. + */ + + if (cmd == F_CANCELLK) { + cmd = F_SETLK; + fl->fl_type = F_UNLCK; + } + + if (IS_GETLK(cmd)) + return dlm_posix_get(conn->cc_lockspace, ino, file, fl); + else if (fl->fl_type == F_UNLCK) + return dlm_posix_unlock(conn->cc_lockspace, ino, file, fl); + else + return dlm_posix_lock(conn->cc_lockspace, ino, file, cmd, fl); +} + /* * Compare a requested locking protocol version against the current one. * @@ -839,6 +871,7 @@ static struct ocfs2_stack_operations ocfs2_user_plugin_ops = { .dlm_unlock = user_dlm_unlock, .lock_status = user_dlm_lock_status, .lock_lvb = user_dlm_lvb, + .plock = user_plock, .dump_lksb = user_dlm_dump_lksb, }; diff --git a/fs/ocfs2/stackglue.c b/fs/ocfs2/stackglue.c index 07f348b8d721..68b668b0e60a 100644 --- a/fs/ocfs2/stackglue.c +++ b/fs/ocfs2/stackglue.c @@ -288,6 +288,26 @@ void ocfs2_dlm_dump_lksb(union ocfs2_dlm_lksb *lksb) } EXPORT_SYMBOL_GPL(ocfs2_dlm_dump_lksb); +int ocfs2_stack_supports_plocks(void) +{ + return active_stack && active_stack->sp_ops->plock; +} +EXPORT_SYMBOL_GPL(ocfs2_stack_supports_plocks); + +/* + * ocfs2_plock() can only be safely called if + * ocfs2_stack_supports_plocks() returned true + */ +int ocfs2_plock(struct ocfs2_cluster_connection *conn, u64 ino, + struct file *file, int cmd, struct file_lock *fl) +{ + WARN_ON_ONCE(active_stack->sp_ops->plock == NULL); + if (active_stack->sp_ops->plock) + return active_stack->sp_ops->plock(conn, ino, file, cmd, fl); + return -EOPNOTSUPP; +} +EXPORT_SYMBOL_GPL(ocfs2_plock); + int ocfs2_cluster_connect(const char *stack_name, const char *group, int grouplen, diff --git a/fs/ocfs2/stackglue.h b/fs/ocfs2/stackglue.h index db56281dd1be..c571af375ef8 100644 --- a/fs/ocfs2/stackglue.h +++ b/fs/ocfs2/stackglue.h @@ -28,6 +28,10 @@ #include "dlm/dlmapi.h" #include <linux/dlm.h> +/* Needed for plock-related prototypes */ +struct file; +struct file_lock; + /* * dlmconstants.h does not have a LOCAL flag. We hope to remove it * some day, but right now we need it. Let's fake it. This value is larger @@ -187,6 +191,17 @@ struct ocfs2_stack_operations { void *(*lock_lvb)(union ocfs2_dlm_lksb *lksb); /* + * Cluster-aware posix locks + * + * This is NULL for stacks which do not support posix locks. + */ + int (*plock)(struct ocfs2_cluster_connection *conn, + u64 ino, + struct file *file, + int cmd, + struct file_lock *fl); + + /* * This is an optoinal debugging hook. If provided, the * stack can dump debugging information about this lock. */ @@ -240,6 +255,10 @@ int ocfs2_dlm_lock_status(union ocfs2_dlm_lksb *lksb); void *ocfs2_dlm_lvb(union ocfs2_dlm_lksb *lksb); void ocfs2_dlm_dump_lksb(union ocfs2_dlm_lksb *lksb); +int ocfs2_stack_supports_plocks(void); +int ocfs2_plock(struct ocfs2_cluster_connection *conn, u64 ino, + struct file *file, int cmd, struct file_lock *fl); + void ocfs2_stack_glue_set_locking_protocol(struct ocfs2_locking_protocol *proto); diff --git a/fs/ocfs2/suballoc.c b/fs/ocfs2/suballoc.c index d2d278fb9819..c5ff18b46b57 100644 --- a/fs/ocfs2/suballoc.c +++ b/fs/ocfs2/suballoc.c @@ -62,15 +62,18 @@ static int ocfs2_block_group_fill(handle_t *handle, struct ocfs2_chain_list *cl); static int ocfs2_block_group_alloc(struct ocfs2_super *osb, struct inode *alloc_inode, - struct buffer_head *bh); + struct buffer_head *bh, + u64 max_block); static int ocfs2_cluster_group_search(struct inode *inode, struct buffer_head *group_bh, u32 bits_wanted, u32 min_bits, + u64 max_block, u16 *bit_off, u16 *bits_found); static int ocfs2_block_group_search(struct inode *inode, struct buffer_head *group_bh, u32 bits_wanted, u32 min_bits, + u64 max_block, u16 *bit_off, u16 *bits_found); static int ocfs2_claim_suballoc_bits(struct ocfs2_super *osb, struct ocfs2_alloc_context *ac, @@ -110,8 +113,11 @@ static inline void ocfs2_block_to_cluster_group(struct inode *inode, u64 data_blkno, u64 *bg_blkno, u16 *bg_bit_off); +static int ocfs2_reserve_clusters_with_limit(struct ocfs2_super *osb, + u32 bits_wanted, u64 max_block, + struct ocfs2_alloc_context **ac); -static void ocfs2_free_ac_resource(struct ocfs2_alloc_context *ac) +void ocfs2_free_ac_resource(struct ocfs2_alloc_context *ac) { struct inode *inode = ac->ac_inode; @@ -124,10 +130,8 @@ static void ocfs2_free_ac_resource(struct ocfs2_alloc_context *ac) iput(inode); ac->ac_inode = NULL; } - if (ac->ac_bh) { - brelse(ac->ac_bh); - ac->ac_bh = NULL; - } + brelse(ac->ac_bh); + ac->ac_bh = NULL; } void ocfs2_free_alloc_context(struct ocfs2_alloc_context *ac) @@ -276,7 +280,8 @@ static inline u16 ocfs2_find_smallest_chain(struct ocfs2_chain_list *cl) */ static int ocfs2_block_group_alloc(struct ocfs2_super *osb, struct inode *alloc_inode, - struct buffer_head *bh) + struct buffer_head *bh, + u64 max_block) { int status, credits; struct ocfs2_dinode *fe = (struct ocfs2_dinode *) bh->b_data; @@ -294,9 +299,9 @@ static int ocfs2_block_group_alloc(struct ocfs2_super *osb, mlog_entry_void(); cl = &fe->id2.i_chain; - status = ocfs2_reserve_clusters(osb, - le16_to_cpu(cl->cl_cpg), - &ac); + status = ocfs2_reserve_clusters_with_limit(osb, + le16_to_cpu(cl->cl_cpg), + max_block, &ac); if (status < 0) { if (status != -ENOSPC) mlog_errno(status); @@ -394,8 +399,7 @@ bail: if (ac) ocfs2_free_alloc_context(ac); - if (bg_bh) - brelse(bg_bh); + brelse(bg_bh); mlog_exit(status); return status; @@ -469,7 +473,8 @@ static int ocfs2_reserve_suballoc_bits(struct ocfs2_super *osb, goto bail; } - status = ocfs2_block_group_alloc(osb, alloc_inode, bh); + status = ocfs2_block_group_alloc(osb, alloc_inode, bh, + ac->ac_max_block); if (status < 0) { if (status != -ENOSPC) mlog_errno(status); @@ -486,16 +491,15 @@ static int ocfs2_reserve_suballoc_bits(struct ocfs2_super *osb, get_bh(bh); ac->ac_bh = bh; bail: - if (bh) - brelse(bh); + brelse(bh); mlog_exit(status); return status; } -int ocfs2_reserve_new_metadata(struct ocfs2_super *osb, - struct ocfs2_dinode *fe, - struct ocfs2_alloc_context **ac) +int ocfs2_reserve_new_metadata_blocks(struct ocfs2_super *osb, + int blocks, + struct ocfs2_alloc_context **ac) { int status; u32 slot; @@ -507,7 +511,7 @@ int ocfs2_reserve_new_metadata(struct ocfs2_super *osb, goto bail; } - (*ac)->ac_bits_wanted = ocfs2_extend_meta_needed(fe); + (*ac)->ac_bits_wanted = blocks; (*ac)->ac_which = OCFS2_AC_USE_META; slot = osb->slot_num; (*ac)->ac_group_search = ocfs2_block_group_search; @@ -532,6 +536,15 @@ bail: return status; } +int ocfs2_reserve_new_metadata(struct ocfs2_super *osb, + struct ocfs2_extent_list *root_el, + struct ocfs2_alloc_context **ac) +{ + return ocfs2_reserve_new_metadata_blocks(osb, + ocfs2_extend_meta_needed(root_el), + ac); +} + static int ocfs2_steal_inode_from_other_nodes(struct ocfs2_super *osb, struct ocfs2_alloc_context *ac) { @@ -582,6 +595,14 @@ int ocfs2_reserve_new_inode(struct ocfs2_super *osb, (*ac)->ac_group_search = ocfs2_block_group_search; /* + * stat(2) can't handle i_ino > 32bits, so we tell the + * lower levels not to allocate us a block group past that + * limit. The 'inode64' mount option avoids this behavior. + */ + if (!(osb->s_mount_opt & OCFS2_MOUNT_INODE64)) + (*ac)->ac_max_block = (u32)~0U; + + /* * slot is set when we successfully steal inode from other nodes. * It is reset in 3 places: * 1. when we flush the truncate log @@ -661,9 +682,9 @@ bail: /* Callers don't need to care which bitmap (local alloc or main) to * use so we figure it out for them, but unfortunately this clutters * things a bit. */ -int ocfs2_reserve_clusters(struct ocfs2_super *osb, - u32 bits_wanted, - struct ocfs2_alloc_context **ac) +static int ocfs2_reserve_clusters_with_limit(struct ocfs2_super *osb, + u32 bits_wanted, u64 max_block, + struct ocfs2_alloc_context **ac) { int status; @@ -677,24 +698,20 @@ int ocfs2_reserve_clusters(struct ocfs2_super *osb, } (*ac)->ac_bits_wanted = bits_wanted; + (*ac)->ac_max_block = max_block; status = -ENOSPC; if (ocfs2_alloc_should_use_local(osb, bits_wanted)) { status = ocfs2_reserve_local_alloc_bits(osb, bits_wanted, *ac); - if ((status < 0) && (status != -ENOSPC)) { + if (status == -EFBIG) { + /* The local alloc window is outside ac_max_block. + * use the main bitmap. */ + status = -ENOSPC; + } else if ((status < 0) && (status != -ENOSPC)) { mlog_errno(status); goto bail; - } else if (status == -ENOSPC) { - /* reserve_local_bits will return enospc with - * the local alloc inode still locked, so we - * can change this safely here. */ - mlog(0, "Disabling local alloc\n"); - /* We set to OCFS2_LA_DISABLED so that umount - * can clean up what's left of the local - * allocation */ - osb->local_alloc_state = OCFS2_LA_DISABLED; } } @@ -718,6 +735,13 @@ bail: return status; } +int ocfs2_reserve_clusters(struct ocfs2_super *osb, + u32 bits_wanted, + struct ocfs2_alloc_context **ac) +{ + return ocfs2_reserve_clusters_with_limit(osb, bits_wanted, 0, ac); +} + /* * More or less lifted from ext3. I'll leave their description below: * @@ -1000,11 +1024,14 @@ static inline int ocfs2_block_group_reasonably_empty(struct ocfs2_group_desc *bg static int ocfs2_cluster_group_search(struct inode *inode, struct buffer_head *group_bh, u32 bits_wanted, u32 min_bits, + u64 max_block, u16 *bit_off, u16 *bits_found) { int search = -ENOSPC; int ret; + u64 blkoff; struct ocfs2_group_desc *gd = (struct ocfs2_group_desc *) group_bh->b_data; + struct ocfs2_super *osb = OCFS2_SB(inode->i_sb); u16 tmp_off, tmp_found; unsigned int max_bits, gd_cluster_off; @@ -1037,6 +1064,17 @@ static int ocfs2_cluster_group_search(struct inode *inode, if (ret) return ret; + if (max_block) { + blkoff = ocfs2_clusters_to_blocks(inode->i_sb, + gd_cluster_off + + tmp_off + tmp_found); + mlog(0, "Checking %llu against %llu\n", + (unsigned long long)blkoff, + (unsigned long long)max_block); + if (blkoff > max_block) + return -ENOSPC; + } + /* ocfs2_block_group_find_clear_bits() might * return success, but we still want to return * -ENOSPC unless it found the minimum number @@ -1045,6 +1083,12 @@ static int ocfs2_cluster_group_search(struct inode *inode, *bit_off = tmp_off; *bits_found = tmp_found; search = 0; /* success */ + } else if (tmp_found) { + /* + * Don't show bits which we'll be returning + * for allocation to the local alloc bitmap. + */ + ocfs2_local_alloc_seen_free_bits(osb, tmp_found); } } @@ -1054,19 +1098,31 @@ static int ocfs2_cluster_group_search(struct inode *inode, static int ocfs2_block_group_search(struct inode *inode, struct buffer_head *group_bh, u32 bits_wanted, u32 min_bits, + u64 max_block, u16 *bit_off, u16 *bits_found) { int ret = -ENOSPC; + u64 blkoff; struct ocfs2_group_desc *bg = (struct ocfs2_group_desc *) group_bh->b_data; BUG_ON(min_bits != 1); BUG_ON(ocfs2_is_cluster_bitmap(inode)); - if (bg->bg_free_bits_count) + if (bg->bg_free_bits_count) { ret = ocfs2_block_group_find_clear_bits(OCFS2_SB(inode->i_sb), group_bh, bits_wanted, le16_to_cpu(bg->bg_bits), bit_off, bits_found); + if (!ret && max_block) { + blkoff = le64_to_cpu(bg->bg_blkno) + *bit_off + + *bits_found; + mlog(0, "Checking %llu against %llu\n", + (unsigned long long)blkoff, + (unsigned long long)max_block); + if (blkoff > max_block) + ret = -ENOSPC; + } + } return ret; } @@ -1116,8 +1172,7 @@ static int ocfs2_search_one_group(struct ocfs2_alloc_context *ac, struct ocfs2_group_desc *gd; struct inode *alloc_inode = ac->ac_inode; - ret = ocfs2_read_block(OCFS2_SB(alloc_inode->i_sb), gd_blkno, - &group_bh, OCFS2_BH_CACHED, alloc_inode); + ret = ocfs2_read_block(alloc_inode, gd_blkno, &group_bh); if (ret < 0) { mlog_errno(ret); return ret; @@ -1131,7 +1186,7 @@ static int ocfs2_search_one_group(struct ocfs2_alloc_context *ac, } ret = ac->ac_group_search(alloc_inode, group_bh, bits_wanted, min_bits, - bit_off, &found); + ac->ac_max_block, bit_off, &found); if (ret < 0) { if (ret != -ENOSPC) mlog_errno(ret); @@ -1186,9 +1241,9 @@ static int ocfs2_search_chain(struct ocfs2_alloc_context *ac, bits_wanted, chain, (unsigned long long)OCFS2_I(alloc_inode)->ip_blkno); - status = ocfs2_read_block(OCFS2_SB(alloc_inode->i_sb), + status = ocfs2_read_block(alloc_inode, le64_to_cpu(cl->cl_recs[chain].c_blkno), - &group_bh, OCFS2_BH_CACHED, alloc_inode); + &group_bh); if (status < 0) { mlog_errno(status); goto bail; @@ -1204,21 +1259,20 @@ static int ocfs2_search_chain(struct ocfs2_alloc_context *ac, /* for now, the chain search is a bit simplistic. We just use * the 1st group with any empty bits. */ while ((status = ac->ac_group_search(alloc_inode, group_bh, - bits_wanted, min_bits, bit_off, + bits_wanted, min_bits, + ac->ac_max_block, bit_off, &tmp_bits)) == -ENOSPC) { if (!bg->bg_next_group) break; - if (prev_group_bh) { - brelse(prev_group_bh); - prev_group_bh = NULL; - } + brelse(prev_group_bh); + prev_group_bh = NULL; + next_group = le64_to_cpu(bg->bg_next_group); prev_group_bh = group_bh; group_bh = NULL; - status = ocfs2_read_block(OCFS2_SB(alloc_inode->i_sb), - next_group, &group_bh, - OCFS2_BH_CACHED, alloc_inode); + status = ocfs2_read_block(alloc_inode, + next_group, &group_bh); if (status < 0) { mlog_errno(status); goto bail; @@ -1307,10 +1361,8 @@ static int ocfs2_search_chain(struct ocfs2_alloc_context *ac, *bg_blkno = le64_to_cpu(bg->bg_blkno); *bits_left = le16_to_cpu(bg->bg_free_bits_count); bail: - if (group_bh) - brelse(group_bh); - if (prev_group_bh) - brelse(prev_group_bh); + brelse(group_bh); + brelse(prev_group_bh); mlog_exit(status); return status; @@ -1723,7 +1775,6 @@ int ocfs2_free_suballoc_bits(handle_t *handle, { int status = 0; u32 tmp_used; - struct ocfs2_super *osb = OCFS2_SB(alloc_inode->i_sb); struct ocfs2_dinode *fe = (struct ocfs2_dinode *) alloc_bh->b_data; struct ocfs2_chain_list *cl = &fe->id2.i_chain; struct buffer_head *group_bh = NULL; @@ -1742,8 +1793,7 @@ int ocfs2_free_suballoc_bits(handle_t *handle, (unsigned long long)OCFS2_I(alloc_inode)->ip_blkno, count, (unsigned long long)bg_blkno, start_bit); - status = ocfs2_read_block(osb, bg_blkno, &group_bh, OCFS2_BH_CACHED, - alloc_inode); + status = ocfs2_read_block(alloc_inode, bg_blkno, &group_bh); if (status < 0) { mlog_errno(status); goto bail; @@ -1784,8 +1834,7 @@ int ocfs2_free_suballoc_bits(handle_t *handle, } bail: - if (group_bh) - brelse(group_bh); + brelse(group_bh); mlog_exit(status); return status; @@ -1838,9 +1887,15 @@ int ocfs2_free_clusters(handle_t *handle, status = ocfs2_free_suballoc_bits(handle, bitmap_inode, bitmap_bh, bg_start_bit, bg_blkno, num_clusters); - if (status < 0) + if (status < 0) { mlog_errno(status); + goto out; + } + ocfs2_local_alloc_seen_free_bits(OCFS2_SB(bitmap_inode->i_sb), + num_clusters); + +out: mlog_exit(status); return status; } @@ -1891,3 +1946,84 @@ static inline void ocfs2_debug_suballoc_inode(struct ocfs2_dinode *fe) (unsigned long long)fe->id2.i_chain.cl_recs[i].c_blkno); } } + +/* + * For a given allocation, determine which allocators will need to be + * accessed, and lock them, reserving the appropriate number of bits. + * + * Sparse file systems call this from ocfs2_write_begin_nolock() + * and ocfs2_allocate_unwritten_extents(). + * + * File systems which don't support holes call this from + * ocfs2_extend_allocation(). + */ +int ocfs2_lock_allocators(struct inode *inode, + struct ocfs2_extent_tree *et, + u32 clusters_to_add, u32 extents_to_split, + struct ocfs2_alloc_context **data_ac, + struct ocfs2_alloc_context **meta_ac) +{ + int ret = 0, num_free_extents; + unsigned int max_recs_needed = clusters_to_add + 2 * extents_to_split; + struct ocfs2_super *osb = OCFS2_SB(inode->i_sb); + + *meta_ac = NULL; + if (data_ac) + *data_ac = NULL; + + BUG_ON(clusters_to_add != 0 && data_ac == NULL); + + num_free_extents = ocfs2_num_free_extents(osb, inode, et); + if (num_free_extents < 0) { + ret = num_free_extents; + mlog_errno(ret); + goto out; + } + + /* + * Sparse allocation file systems need to be more conservative + * with reserving room for expansion - the actual allocation + * happens while we've got a journal handle open so re-taking + * a cluster lock (because we ran out of room for another + * extent) will violate ordering rules. + * + * Most of the time we'll only be seeing this 1 cluster at a time + * anyway. + * + * Always lock for any unwritten extents - we might want to + * add blocks during a split. + */ + if (!num_free_extents || + (ocfs2_sparse_alloc(osb) && num_free_extents < max_recs_needed)) { + ret = ocfs2_reserve_new_metadata(osb, et->et_root_el, meta_ac); + if (ret < 0) { + if (ret != -ENOSPC) + mlog_errno(ret); + goto out; + } + } + + if (clusters_to_add == 0) + goto out; + + ret = ocfs2_reserve_clusters(osb, clusters_to_add, data_ac); + if (ret < 0) { + if (ret != -ENOSPC) + mlog_errno(ret); + goto out; + } + +out: + if (ret) { + if (*meta_ac) { + ocfs2_free_alloc_context(*meta_ac); + *meta_ac = NULL; + } + + /* + * We cannot have an error and a non null *data_ac. + */ + } + + return ret; +} diff --git a/fs/ocfs2/suballoc.h b/fs/ocfs2/suballoc.h index 544c600662bd..4df159d8f450 100644 --- a/fs/ocfs2/suballoc.h +++ b/fs/ocfs2/suballoc.h @@ -28,10 +28,11 @@ typedef int (group_search_t)(struct inode *, struct buffer_head *, - u32, - u32, - u16 *, - u16 *); + u32, /* bits_wanted */ + u32, /* min_bits */ + u64, /* max_block */ + u16 *, /* *bit_off */ + u16 *); /* *bits_found */ struct ocfs2_alloc_context { struct inode *ac_inode; /* which bitmap are we allocating from? */ @@ -51,6 +52,8 @@ struct ocfs2_alloc_context { group_search_t *ac_group_search; u64 ac_last_group; + u64 ac_max_block; /* Highest block number to allocate. 0 is + is the same as ~0 - unlimited */ }; void ocfs2_free_alloc_context(struct ocfs2_alloc_context *ac); @@ -59,9 +62,17 @@ static inline int ocfs2_alloc_context_bits_left(struct ocfs2_alloc_context *ac) return ac->ac_bits_wanted - ac->ac_bits_given; } +/* + * Please note that the caller must make sure that root_el is the root + * of extent tree. So for an inode, it should be &fe->id2.i_list. Otherwise + * the result may be wrong. + */ int ocfs2_reserve_new_metadata(struct ocfs2_super *osb, - struct ocfs2_dinode *fe, + struct ocfs2_extent_list *root_el, struct ocfs2_alloc_context **ac); +int ocfs2_reserve_new_metadata_blocks(struct ocfs2_super *osb, + int blocks, + struct ocfs2_alloc_context **ac); int ocfs2_reserve_new_inode(struct ocfs2_super *osb, struct ocfs2_alloc_context **ac); int ocfs2_reserve_clusters(struct ocfs2_super *osb, @@ -147,6 +158,7 @@ static inline int ocfs2_is_cluster_bitmap(struct inode *inode) * apis above. */ int ocfs2_reserve_cluster_bitmap_bits(struct ocfs2_super *osb, struct ocfs2_alloc_context *ac); +void ocfs2_free_ac_resource(struct ocfs2_alloc_context *ac); /* given a cluster offset, calculate which block group it belongs to * and return that block offset. */ @@ -156,4 +168,8 @@ u64 ocfs2_which_cluster_group(struct inode *inode, u32 cluster); int ocfs2_check_group_descriptor(struct super_block *sb, struct ocfs2_dinode *di, struct ocfs2_group_desc *gd); +int ocfs2_lock_allocators(struct inode *inode, struct ocfs2_extent_tree *et, + u32 clusters_to_add, u32 extents_to_split, + struct ocfs2_alloc_context **data_ac, + struct ocfs2_alloc_context **meta_ac); #endif /* _CHAINALLOC_H_ */ diff --git a/fs/ocfs2/super.c b/fs/ocfs2/super.c index 70334d85aff1..304b63ac78cf 100644 --- a/fs/ocfs2/super.c +++ b/fs/ocfs2/super.c @@ -64,6 +64,7 @@ #include "sysfile.h" #include "uptodate.h" #include "ver.h" +#include "xattr.h" #include "buffer_head_io.h" @@ -154,6 +155,9 @@ enum { Opt_localalloc, Opt_localflocks, Opt_stack, + Opt_user_xattr, + Opt_nouser_xattr, + Opt_inode64, Opt_err, }; @@ -173,6 +177,9 @@ static const match_table_t tokens = { {Opt_localalloc, "localalloc=%d"}, {Opt_localflocks, "localflocks"}, {Opt_stack, "cluster_stack=%s"}, + {Opt_user_xattr, "user_xattr"}, + {Opt_nouser_xattr, "nouser_xattr"}, + {Opt_inode64, "inode64"}, {Opt_err, NULL} }; @@ -205,10 +212,11 @@ static int ocfs2_sync_fs(struct super_block *sb, int wait) ocfs2_schedule_truncate_log_flush(osb, 0); } - if (journal_start_commit(OCFS2_SB(sb)->journal->j_journal, &target)) { + if (jbd2_journal_start_commit(OCFS2_SB(sb)->journal->j_journal, + &target)) { if (wait) - log_wait_commit(OCFS2_SB(sb)->journal->j_journal, - target); + jbd2_log_wait_commit(OCFS2_SB(sb)->journal->j_journal, + target); } return 0; } @@ -325,6 +333,7 @@ static struct inode *ocfs2_alloc_inode(struct super_block *sb) if (!oi) return NULL; + jbd2_journal_init_jbd_inode(&oi->ip_jinode, &oi->vfs_inode); return &oi->vfs_inode; } @@ -406,6 +415,15 @@ static int ocfs2_remount(struct super_block *sb, int *flags, char *data) goto out; } + /* Probably don't want this on remount; it might + * mess with other nodes */ + if (!(osb->s_mount_opt & OCFS2_MOUNT_INODE64) && + (parsed_options.mount_opt & OCFS2_MOUNT_INODE64)) { + ret = -EINVAL; + mlog(ML_ERROR, "Cannot enable inode64 on remount\n"); + goto out; + } + /* We're going to/from readonly mode. */ if ((*flags & MS_RDONLY) != (sb->s_flags & MS_RDONLY)) { /* Lock here so the check of HARD_RO and the potential @@ -637,7 +655,8 @@ static int ocfs2_fill_super(struct super_block *sb, void *data, int silent) osb->s_atime_quantum = parsed_options.atime_quantum; osb->preferred_slot = parsed_options.slot; osb->osb_commit_interval = parsed_options.commit_interval; - osb->local_alloc_size = parsed_options.localalloc_opt; + osb->local_alloc_default_bits = ocfs2_megabytes_to_clusters(sb, parsed_options.localalloc_opt); + osb->local_alloc_bits = osb->local_alloc_default_bits; status = ocfs2_verify_userspace_stack(osb, &parsed_options); if (status) @@ -743,8 +762,7 @@ static int ocfs2_fill_super(struct super_block *sb, void *data, int silent) return status; read_super_error: - if (bh != NULL) - brelse(bh); + brelse(bh); if (inode) iput(inode); @@ -847,6 +865,12 @@ static int ocfs2_parse_options(struct super_block *sb, case Opt_data_writeback: mopt->mount_opt |= OCFS2_MOUNT_DATA_WRITEBACK; break; + case Opt_user_xattr: + mopt->mount_opt &= ~OCFS2_MOUNT_NOUSERXATTR; + break; + case Opt_nouser_xattr: + mopt->mount_opt |= OCFS2_MOUNT_NOUSERXATTR; + break; case Opt_atime_quantum: if (match_int(&args[0], &option)) { status = 0; @@ -873,7 +897,7 @@ static int ocfs2_parse_options(struct super_block *sb, if (option < 0) return 0; if (option == 0) - option = JBD_DEFAULT_MAX_COMMIT_AGE; + option = JBD2_DEFAULT_MAX_COMMIT_AGE; mopt->commit_interval = HZ * option; break; case Opt_localalloc: @@ -918,6 +942,9 @@ static int ocfs2_parse_options(struct super_block *sb, OCFS2_STACK_LABEL_LEN); mopt->cluster_stack[OCFS2_STACK_LABEL_LEN] = '\0'; break; + case Opt_inode64: + mopt->mount_opt |= OCFS2_MOUNT_INODE64; + break; default: mlog(ML_ERROR, "Unrecognized mount option \"%s\" " @@ -938,6 +965,7 @@ static int ocfs2_show_options(struct seq_file *s, struct vfsmount *mnt) { struct ocfs2_super *osb = OCFS2_SB(mnt->mnt_sb); unsigned long opts = osb->s_mount_opt; + unsigned int local_alloc_megs; if (opts & OCFS2_MOUNT_HB_LOCAL) seq_printf(s, ",_netdev,heartbeat=local"); @@ -970,8 +998,9 @@ static int ocfs2_show_options(struct seq_file *s, struct vfsmount *mnt) seq_printf(s, ",commit=%u", (unsigned) (osb->osb_commit_interval / HZ)); - if (osb->local_alloc_size != OCFS2_DEFAULT_LOCAL_ALLOC_SIZE) - seq_printf(s, ",localalloc=%d", osb->local_alloc_size); + local_alloc_megs = osb->local_alloc_bits >> (20 - osb->s_clustersize_bits); + if (local_alloc_megs != OCFS2_DEFAULT_LOCAL_ALLOC_SIZE) + seq_printf(s, ",localalloc=%d", local_alloc_megs); if (opts & OCFS2_MOUNT_LOCALFLOCKS) seq_printf(s, ",localflocks,"); @@ -980,6 +1009,14 @@ static int ocfs2_show_options(struct seq_file *s, struct vfsmount *mnt) seq_printf(s, ",cluster_stack=%.*s", OCFS2_STACK_LABEL_LEN, osb->osb_cluster_stack); + if (opts & OCFS2_MOUNT_NOUSERXATTR) + seq_printf(s, ",nouser_xattr"); + else + seq_printf(s, ",user_xattr"); + + if (opts & OCFS2_MOUNT_INODE64) + seq_printf(s, ",inode64"); + return 0; } @@ -1132,6 +1169,7 @@ static void ocfs2_inode_init_once(void *data) oi->ip_dir_start_lookup = 0; init_rwsem(&oi->ip_alloc_sem); + init_rwsem(&oi->ip_xattr_sem); mutex_init(&oi->ip_io_mutex); oi->ip_blkno = 0ULL; @@ -1375,6 +1413,7 @@ static int ocfs2_initialize_super(struct super_block *sb, sb->s_fs_info = osb; sb->s_op = &ocfs2_sops; sb->s_export_op = &ocfs2_export_ops; + sb->s_xattr = ocfs2_xattr_handlers; sb->s_time_gran = 1; sb->s_flags |= MS_NOATIME; /* this is needed to support O_LARGEFILE */ @@ -1421,8 +1460,12 @@ static int ocfs2_initialize_super(struct super_block *sb, osb->slot_num = OCFS2_INVALID_SLOT; + osb->s_xattr_inline_size = le16_to_cpu( + di->id2.i_super.s_xattr_inline_size); + osb->local_alloc_state = OCFS2_LA_UNUSED; osb->local_alloc_bh = NULL; + INIT_DELAYED_WORK(&osb->la_enable_wq, ocfs2_la_enable_worker); init_waitqueue_head(&osb->osb_mount_event); @@ -1568,6 +1611,7 @@ static int ocfs2_initialize_super(struct super_block *sb, osb->first_cluster_group_blkno = le64_to_cpu(di->id2.i_super.s_first_cluster_group); osb->fs_generation = le32_to_cpu(di->i_fs_generation); + osb->uuid_hash = le32_to_cpu(di->id2.i_super.s_uuid_hash); mlog(0, "vol_label: %s\n", osb->vol_label); mlog(0, "uuid: %s\n", osb->uuid_str); mlog(0, "root_blkno=%llu, system_dir_blkno=%llu\n", diff --git a/fs/ocfs2/symlink.c b/fs/ocfs2/symlink.c index ba9dbb51d25b..cbd03dfdc7b9 100644 --- a/fs/ocfs2/symlink.c +++ b/fs/ocfs2/symlink.c @@ -50,6 +50,7 @@ #include "inode.h" #include "journal.h" #include "symlink.h" +#include "xattr.h" #include "buffer_head_io.h" @@ -83,11 +84,7 @@ static char *ocfs2_fast_symlink_getlink(struct inode *inode, mlog_entry_void(); - status = ocfs2_read_block(OCFS2_SB(inode->i_sb), - OCFS2_I(inode)->ip_blkno, - bh, - OCFS2_BH_CACHED, - inode); + status = ocfs2_read_block(inode, OCFS2_I(inode)->ip_blkno, bh); if (status < 0) { mlog_errno(status); link = ERR_PTR(status); @@ -157,8 +154,7 @@ bail: kunmap(page); page_cache_release(page); } - if (bh) - brelse(bh); + brelse(bh); return ERR_PTR(status); } @@ -168,10 +164,18 @@ const struct inode_operations ocfs2_symlink_inode_operations = { .follow_link = ocfs2_follow_link, .getattr = ocfs2_getattr, .setattr = ocfs2_setattr, + .setxattr = generic_setxattr, + .getxattr = generic_getxattr, + .listxattr = ocfs2_listxattr, + .removexattr = generic_removexattr, }; const struct inode_operations ocfs2_fast_symlink_inode_operations = { .readlink = ocfs2_readlink, .follow_link = ocfs2_follow_link, .getattr = ocfs2_getattr, .setattr = ocfs2_setattr, + .setxattr = generic_setxattr, + .getxattr = generic_getxattr, + .listxattr = ocfs2_listxattr, + .removexattr = generic_removexattr, }; diff --git a/fs/ocfs2/uptodate.c b/fs/ocfs2/uptodate.c index 4da8851f2b23..187b99ff0368 100644 --- a/fs/ocfs2/uptodate.c +++ b/fs/ocfs2/uptodate.c @@ -53,7 +53,11 @@ #include <linux/highmem.h> #include <linux/buffer_head.h> #include <linux/rbtree.h> -#include <linux/jbd.h> +#ifndef CONFIG_OCFS2_COMPAT_JBD +# include <linux/jbd2.h> +#else +# include <linux/jbd.h> +#endif #define MLOG_MASK_PREFIX ML_UPTODATE @@ -511,14 +515,10 @@ static void ocfs2_remove_metadata_tree(struct ocfs2_caching_info *ci, ci->ci_num_cached--; } -/* Called when we remove a chunk of metadata from an inode. We don't - * bother reverting things to an inlined array in the case of a remove - * which moves us back under the limit. */ -void ocfs2_remove_from_cache(struct inode *inode, - struct buffer_head *bh) +static void ocfs2_remove_block_from_cache(struct inode *inode, + sector_t block) { int index; - sector_t block = bh->b_blocknr; struct ocfs2_meta_cache_item *item = NULL; struct ocfs2_inode_info *oi = OCFS2_I(inode); struct ocfs2_caching_info *ci = &oi->ip_metadata_cache; @@ -544,6 +544,30 @@ void ocfs2_remove_from_cache(struct inode *inode, kmem_cache_free(ocfs2_uptodate_cachep, item); } +/* + * Called when we remove a chunk of metadata from an inode. We don't + * bother reverting things to an inlined array in the case of a remove + * which moves us back under the limit. + */ +void ocfs2_remove_from_cache(struct inode *inode, + struct buffer_head *bh) +{ + sector_t block = bh->b_blocknr; + + ocfs2_remove_block_from_cache(inode, block); +} + +/* Called when we remove xattr clusters from an inode. */ +void ocfs2_remove_xattr_clusters_from_cache(struct inode *inode, + sector_t block, + u32 c_len) +{ + unsigned int i, b_len = ocfs2_clusters_to_blocks(inode->i_sb, 1) * c_len; + + for (i = 0; i < b_len; i++, block++) + ocfs2_remove_block_from_cache(inode, block); +} + int __init init_ocfs2_uptodate_cache(void) { ocfs2_uptodate_cachep = kmem_cache_create("ocfs2_uptodate", diff --git a/fs/ocfs2/uptodate.h b/fs/ocfs2/uptodate.h index 2e73206059a8..531b4b3a0c47 100644 --- a/fs/ocfs2/uptodate.h +++ b/fs/ocfs2/uptodate.h @@ -40,6 +40,9 @@ void ocfs2_set_new_buffer_uptodate(struct inode *inode, struct buffer_head *bh); void ocfs2_remove_from_cache(struct inode *inode, struct buffer_head *bh); +void ocfs2_remove_xattr_clusters_from_cache(struct inode *inode, + sector_t block, + u32 c_len); int ocfs2_buffer_read_ahead(struct inode *inode, struct buffer_head *bh); diff --git a/fs/ocfs2/xattr.c b/fs/ocfs2/xattr.c new file mode 100644 index 000000000000..c25780a70dfd --- /dev/null +++ b/fs/ocfs2/xattr.c @@ -0,0 +1,4834 @@ +/* -*- mode: c; c-basic-offset: 8; -*- + * vim: noexpandtab sw=8 ts=8 sts=0: + * + * xattr.c + * + * Copyright (C) 2008 Oracle. All rights reserved. + * + * CREDITS: + * Lots of code in this file is taken from ext3. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public + * License along with this program; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 021110-1307, USA. + */ + +#include <linux/capability.h> +#include <linux/fs.h> +#include <linux/types.h> +#include <linux/slab.h> +#include <linux/highmem.h> +#include <linux/pagemap.h> +#include <linux/uio.h> +#include <linux/sched.h> +#include <linux/splice.h> +#include <linux/mount.h> +#include <linux/writeback.h> +#include <linux/falloc.h> +#include <linux/sort.h> +#include <linux/init.h> +#include <linux/module.h> +#include <linux/string.h> + +#define MLOG_MASK_PREFIX ML_XATTR +#include <cluster/masklog.h> + +#include "ocfs2.h" +#include "alloc.h" +#include "dlmglue.h" +#include "file.h" +#include "symlink.h" +#include "sysfile.h" +#include "inode.h" +#include "journal.h" +#include "ocfs2_fs.h" +#include "suballoc.h" +#include "uptodate.h" +#include "buffer_head_io.h" +#include "super.h" +#include "xattr.h" + + +struct ocfs2_xattr_def_value_root { + struct ocfs2_xattr_value_root xv; + struct ocfs2_extent_rec er; +}; + +struct ocfs2_xattr_bucket { + struct buffer_head *bhs[OCFS2_XATTR_MAX_BLOCKS_PER_BUCKET]; + struct ocfs2_xattr_header *xh; +}; + +#define OCFS2_XATTR_ROOT_SIZE (sizeof(struct ocfs2_xattr_def_value_root)) +#define OCFS2_XATTR_INLINE_SIZE 80 + +static struct ocfs2_xattr_def_value_root def_xv = { + .xv.xr_list.l_count = cpu_to_le16(1), +}; + +struct xattr_handler *ocfs2_xattr_handlers[] = { + &ocfs2_xattr_user_handler, + &ocfs2_xattr_trusted_handler, + NULL +}; + +static struct xattr_handler *ocfs2_xattr_handler_map[] = { + [OCFS2_XATTR_INDEX_USER] = &ocfs2_xattr_user_handler, + [OCFS2_XATTR_INDEX_TRUSTED] = &ocfs2_xattr_trusted_handler, +}; + +struct ocfs2_xattr_info { + int name_index; + const char *name; + const void *value; + size_t value_len; +}; + +struct ocfs2_xattr_search { + struct buffer_head *inode_bh; + /* + * xattr_bh point to the block buffer head which has extended attribute + * when extended attribute in inode, xattr_bh is equal to inode_bh. + */ + struct buffer_head *xattr_bh; + struct ocfs2_xattr_header *header; + struct ocfs2_xattr_bucket bucket; + void *base; + void *end; + struct ocfs2_xattr_entry *here; + int not_found; +}; + +static int ocfs2_xattr_bucket_get_name_value(struct inode *inode, + struct ocfs2_xattr_header *xh, + int index, + int *block_off, + int *new_offset); + +static int ocfs2_xattr_index_block_find(struct inode *inode, + struct buffer_head *root_bh, + int name_index, + const char *name, + struct ocfs2_xattr_search *xs); + +static int ocfs2_xattr_tree_list_index_block(struct inode *inode, + struct ocfs2_xattr_tree_root *xt, + char *buffer, + size_t buffer_size); + +static int ocfs2_xattr_create_index_block(struct inode *inode, + struct ocfs2_xattr_search *xs); + +static int ocfs2_xattr_set_entry_index_block(struct inode *inode, + struct ocfs2_xattr_info *xi, + struct ocfs2_xattr_search *xs); + +static int ocfs2_delete_xattr_index_block(struct inode *inode, + struct buffer_head *xb_bh); + +static inline const char *ocfs2_xattr_prefix(int name_index) +{ + struct xattr_handler *handler = NULL; + + if (name_index > 0 && name_index < OCFS2_XATTR_MAX) + handler = ocfs2_xattr_handler_map[name_index]; + + return handler ? handler->prefix : NULL; +} + +static u32 ocfs2_xattr_name_hash(struct inode *inode, + const char *name, + int name_len) +{ + /* Get hash value of uuid from super block */ + u32 hash = OCFS2_SB(inode->i_sb)->uuid_hash; + int i; + + /* hash extended attribute name */ + for (i = 0; i < name_len; i++) { + hash = (hash << OCFS2_HASH_SHIFT) ^ + (hash >> (8*sizeof(hash) - OCFS2_HASH_SHIFT)) ^ + *name++; + } + + return hash; +} + +/* + * ocfs2_xattr_hash_entry() + * + * Compute the hash of an extended attribute. + */ +static void ocfs2_xattr_hash_entry(struct inode *inode, + struct ocfs2_xattr_header *header, + struct ocfs2_xattr_entry *entry) +{ + u32 hash = 0; + char *name = (char *)header + le16_to_cpu(entry->xe_name_offset); + + hash = ocfs2_xattr_name_hash(inode, name, entry->xe_name_len); + entry->xe_name_hash = cpu_to_le32(hash); + + return; +} + +static int ocfs2_xattr_extend_allocation(struct inode *inode, + u32 clusters_to_add, + struct buffer_head *xattr_bh, + struct ocfs2_xattr_value_root *xv) +{ + int status = 0; + int restart_func = 0; + int credits = 0; + handle_t *handle = NULL; + struct ocfs2_alloc_context *data_ac = NULL; + struct ocfs2_alloc_context *meta_ac = NULL; + enum ocfs2_alloc_restarted why; + struct ocfs2_super *osb = OCFS2_SB(inode->i_sb); + u32 prev_clusters, logical_start = le32_to_cpu(xv->xr_clusters); + struct ocfs2_extent_tree et; + + mlog(0, "(clusters_to_add for xattr= %u)\n", clusters_to_add); + + ocfs2_init_xattr_value_extent_tree(&et, inode, xattr_bh, xv); + +restart_all: + + status = ocfs2_lock_allocators(inode, &et, clusters_to_add, 0, + &data_ac, &meta_ac); + if (status) { + mlog_errno(status); + goto leave; + } + + credits = ocfs2_calc_extend_credits(osb->sb, et.et_root_el, + clusters_to_add); + handle = ocfs2_start_trans(osb, credits); + if (IS_ERR(handle)) { + status = PTR_ERR(handle); + handle = NULL; + mlog_errno(status); + goto leave; + } + +restarted_transaction: + status = ocfs2_journal_access(handle, inode, xattr_bh, + OCFS2_JOURNAL_ACCESS_WRITE); + if (status < 0) { + mlog_errno(status); + goto leave; + } + + prev_clusters = le32_to_cpu(xv->xr_clusters); + status = ocfs2_add_clusters_in_btree(osb, + inode, + &logical_start, + clusters_to_add, + 0, + &et, + handle, + data_ac, + meta_ac, + &why); + if ((status < 0) && (status != -EAGAIN)) { + if (status != -ENOSPC) + mlog_errno(status); + goto leave; + } + + status = ocfs2_journal_dirty(handle, xattr_bh); + if (status < 0) { + mlog_errno(status); + goto leave; + } + + clusters_to_add -= le32_to_cpu(xv->xr_clusters) - prev_clusters; + + if (why != RESTART_NONE && clusters_to_add) { + if (why == RESTART_META) { + mlog(0, "restarting function.\n"); + restart_func = 1; + } else { + BUG_ON(why != RESTART_TRANS); + + mlog(0, "restarting transaction.\n"); + /* TODO: This can be more intelligent. */ + credits = ocfs2_calc_extend_credits(osb->sb, + et.et_root_el, + clusters_to_add); + status = ocfs2_extend_trans(handle, credits); + if (status < 0) { + /* handle still has to be committed at + * this point. */ + status = -ENOMEM; + mlog_errno(status); + goto leave; + } + goto restarted_transaction; + } + } + +leave: + if (handle) { + ocfs2_commit_trans(osb, handle); + handle = NULL; + } + if (data_ac) { + ocfs2_free_alloc_context(data_ac); + data_ac = NULL; + } + if (meta_ac) { + ocfs2_free_alloc_context(meta_ac); + meta_ac = NULL; + } + if ((!status) && restart_func) { + restart_func = 0; + goto restart_all; + } + + return status; +} + +static int __ocfs2_remove_xattr_range(struct inode *inode, + struct buffer_head *root_bh, + struct ocfs2_xattr_value_root *xv, + u32 cpos, u32 phys_cpos, u32 len, + struct ocfs2_cached_dealloc_ctxt *dealloc) +{ + int ret; + u64 phys_blkno = ocfs2_clusters_to_blocks(inode->i_sb, phys_cpos); + struct ocfs2_super *osb = OCFS2_SB(inode->i_sb); + struct inode *tl_inode = osb->osb_tl_inode; + handle_t *handle; + struct ocfs2_alloc_context *meta_ac = NULL; + struct ocfs2_extent_tree et; + + ocfs2_init_xattr_value_extent_tree(&et, inode, root_bh, xv); + + ret = ocfs2_lock_allocators(inode, &et, 0, 1, NULL, &meta_ac); + if (ret) { + mlog_errno(ret); + return ret; + } + + mutex_lock(&tl_inode->i_mutex); + + if (ocfs2_truncate_log_needs_flush(osb)) { + ret = __ocfs2_flush_truncate_log(osb); + if (ret < 0) { + mlog_errno(ret); + goto out; + } + } + + handle = ocfs2_start_trans(osb, OCFS2_REMOVE_EXTENT_CREDITS); + if (IS_ERR(handle)) { + ret = PTR_ERR(handle); + mlog_errno(ret); + goto out; + } + + ret = ocfs2_journal_access(handle, inode, root_bh, + OCFS2_JOURNAL_ACCESS_WRITE); + if (ret) { + mlog_errno(ret); + goto out_commit; + } + + ret = ocfs2_remove_extent(inode, &et, cpos, len, handle, meta_ac, + dealloc); + if (ret) { + mlog_errno(ret); + goto out_commit; + } + + le32_add_cpu(&xv->xr_clusters, -len); + + ret = ocfs2_journal_dirty(handle, root_bh); + if (ret) { + mlog_errno(ret); + goto out_commit; + } + + ret = ocfs2_truncate_log_append(osb, handle, phys_blkno, len); + if (ret) + mlog_errno(ret); + +out_commit: + ocfs2_commit_trans(osb, handle); +out: + mutex_unlock(&tl_inode->i_mutex); + + if (meta_ac) + ocfs2_free_alloc_context(meta_ac); + + return ret; +} + +static int ocfs2_xattr_shrink_size(struct inode *inode, + u32 old_clusters, + u32 new_clusters, + struct buffer_head *root_bh, + struct ocfs2_xattr_value_root *xv) +{ + int ret = 0; + u32 trunc_len, cpos, phys_cpos, alloc_size; + u64 block; + struct ocfs2_super *osb = OCFS2_SB(inode->i_sb); + struct ocfs2_cached_dealloc_ctxt dealloc; + + ocfs2_init_dealloc_ctxt(&dealloc); + + if (old_clusters <= new_clusters) + return 0; + + cpos = new_clusters; + trunc_len = old_clusters - new_clusters; + while (trunc_len) { + ret = ocfs2_xattr_get_clusters(inode, cpos, &phys_cpos, + &alloc_size, &xv->xr_list); + if (ret) { + mlog_errno(ret); + goto out; + } + + if (alloc_size > trunc_len) + alloc_size = trunc_len; + + ret = __ocfs2_remove_xattr_range(inode, root_bh, xv, cpos, + phys_cpos, alloc_size, + &dealloc); + if (ret) { + mlog_errno(ret); + goto out; + } + + block = ocfs2_clusters_to_blocks(inode->i_sb, phys_cpos); + ocfs2_remove_xattr_clusters_from_cache(inode, block, + alloc_size); + cpos += alloc_size; + trunc_len -= alloc_size; + } + +out: + ocfs2_schedule_truncate_log_flush(osb, 1); + ocfs2_run_deallocs(osb, &dealloc); + + return ret; +} + +static int ocfs2_xattr_value_truncate(struct inode *inode, + struct buffer_head *root_bh, + struct ocfs2_xattr_value_root *xv, + int len) +{ + int ret; + u32 new_clusters = ocfs2_clusters_for_bytes(inode->i_sb, len); + u32 old_clusters = le32_to_cpu(xv->xr_clusters); + + if (new_clusters == old_clusters) + return 0; + + if (new_clusters > old_clusters) + ret = ocfs2_xattr_extend_allocation(inode, + new_clusters - old_clusters, + root_bh, xv); + else + ret = ocfs2_xattr_shrink_size(inode, + old_clusters, new_clusters, + root_bh, xv); + + return ret; +} + +static int ocfs2_xattr_list_entry(char *buffer, size_t size, + size_t *result, const char *prefix, + const char *name, int name_len) +{ + char *p = buffer + *result; + int prefix_len = strlen(prefix); + int total_len = prefix_len + name_len + 1; + + *result += total_len; + + /* we are just looking for how big our buffer needs to be */ + if (!size) + return 0; + + if (*result > size) + return -ERANGE; + + memcpy(p, prefix, prefix_len); + memcpy(p + prefix_len, name, name_len); + p[prefix_len + name_len] = '\0'; + + return 0; +} + +static int ocfs2_xattr_list_entries(struct inode *inode, + struct ocfs2_xattr_header *header, + char *buffer, size_t buffer_size) +{ + size_t result = 0; + int i, type, ret; + const char *prefix, *name; + + for (i = 0 ; i < le16_to_cpu(header->xh_count); i++) { + struct ocfs2_xattr_entry *entry = &header->xh_entries[i]; + type = ocfs2_xattr_get_type(entry); + prefix = ocfs2_xattr_prefix(type); + + if (prefix) { + name = (const char *)header + + le16_to_cpu(entry->xe_name_offset); + + ret = ocfs2_xattr_list_entry(buffer, buffer_size, + &result, prefix, name, + entry->xe_name_len); + if (ret) + return ret; + } + } + + return result; +} + +static int ocfs2_xattr_ibody_list(struct inode *inode, + struct ocfs2_dinode *di, + char *buffer, + size_t buffer_size) +{ + struct ocfs2_xattr_header *header = NULL; + struct ocfs2_inode_info *oi = OCFS2_I(inode); + int ret = 0; + + if (!(oi->ip_dyn_features & OCFS2_INLINE_XATTR_FL)) + return ret; + + header = (struct ocfs2_xattr_header *) + ((void *)di + inode->i_sb->s_blocksize - + le16_to_cpu(di->i_xattr_inline_size)); + + ret = ocfs2_xattr_list_entries(inode, header, buffer, buffer_size); + + return ret; +} + +static int ocfs2_xattr_block_list(struct inode *inode, + struct ocfs2_dinode *di, + char *buffer, + size_t buffer_size) +{ + struct buffer_head *blk_bh = NULL; + struct ocfs2_xattr_block *xb; + int ret = 0; + + if (!di->i_xattr_loc) + return ret; + + ret = ocfs2_read_block(inode, le64_to_cpu(di->i_xattr_loc), &blk_bh); + if (ret < 0) { + mlog_errno(ret); + return ret; + } + /*Verify the signature of xattr block*/ + if (memcmp((void *)blk_bh->b_data, OCFS2_XATTR_BLOCK_SIGNATURE, + strlen(OCFS2_XATTR_BLOCK_SIGNATURE))) { + ret = -EFAULT; + goto cleanup; + } + + xb = (struct ocfs2_xattr_block *)blk_bh->b_data; + + if (!(le16_to_cpu(xb->xb_flags) & OCFS2_XATTR_INDEXED)) { + struct ocfs2_xattr_header *header = &xb->xb_attrs.xb_header; + ret = ocfs2_xattr_list_entries(inode, header, + buffer, buffer_size); + } else { + struct ocfs2_xattr_tree_root *xt = &xb->xb_attrs.xb_root; + ret = ocfs2_xattr_tree_list_index_block(inode, xt, + buffer, buffer_size); + } +cleanup: + brelse(blk_bh); + + return ret; +} + +ssize_t ocfs2_listxattr(struct dentry *dentry, + char *buffer, + size_t size) +{ + int ret = 0, i_ret = 0, b_ret = 0; + struct buffer_head *di_bh = NULL; + struct ocfs2_dinode *di = NULL; + struct ocfs2_inode_info *oi = OCFS2_I(dentry->d_inode); + + if (!ocfs2_supports_xattr(OCFS2_SB(dentry->d_sb))) + return -EOPNOTSUPP; + + if (!(oi->ip_dyn_features & OCFS2_HAS_XATTR_FL)) + return ret; + + ret = ocfs2_inode_lock(dentry->d_inode, &di_bh, 0); + if (ret < 0) { + mlog_errno(ret); + return ret; + } + + di = (struct ocfs2_dinode *)di_bh->b_data; + + down_read(&oi->ip_xattr_sem); + i_ret = ocfs2_xattr_ibody_list(dentry->d_inode, di, buffer, size); + if (i_ret < 0) + b_ret = 0; + else { + if (buffer) { + buffer += i_ret; + size -= i_ret; + } + b_ret = ocfs2_xattr_block_list(dentry->d_inode, di, + buffer, size); + if (b_ret < 0) + i_ret = 0; + } + up_read(&oi->ip_xattr_sem); + ocfs2_inode_unlock(dentry->d_inode, 0); + + brelse(di_bh); + + return i_ret + b_ret; +} + +static int ocfs2_xattr_find_entry(int name_index, + const char *name, + struct ocfs2_xattr_search *xs) +{ + struct ocfs2_xattr_entry *entry; + size_t name_len; + int i, cmp = 1; + + if (name == NULL) + return -EINVAL; + + name_len = strlen(name); + entry = xs->here; + for (i = 0; i < le16_to_cpu(xs->header->xh_count); i++) { + cmp = name_index - ocfs2_xattr_get_type(entry); + if (!cmp) + cmp = name_len - entry->xe_name_len; + if (!cmp) + cmp = memcmp(name, (xs->base + + le16_to_cpu(entry->xe_name_offset)), + name_len); + if (cmp == 0) + break; + entry += 1; + } + xs->here = entry; + + return cmp ? -ENODATA : 0; +} + +static int ocfs2_xattr_get_value_outside(struct inode *inode, + struct ocfs2_xattr_value_root *xv, + void *buffer, + size_t len) +{ + u32 cpos, p_cluster, num_clusters, bpc, clusters; + u64 blkno; + int i, ret = 0; + size_t cplen, blocksize; + struct buffer_head *bh = NULL; + struct ocfs2_extent_list *el; + + el = &xv->xr_list; + clusters = le32_to_cpu(xv->xr_clusters); + bpc = ocfs2_clusters_to_blocks(inode->i_sb, 1); + blocksize = inode->i_sb->s_blocksize; + + cpos = 0; + while (cpos < clusters) { + ret = ocfs2_xattr_get_clusters(inode, cpos, &p_cluster, + &num_clusters, el); + if (ret) { + mlog_errno(ret); + goto out; + } + + blkno = ocfs2_clusters_to_blocks(inode->i_sb, p_cluster); + /* Copy ocfs2_xattr_value */ + for (i = 0; i < num_clusters * bpc; i++, blkno++) { + ret = ocfs2_read_block(inode, blkno, &bh); + if (ret) { + mlog_errno(ret); + goto out; + } + + cplen = len >= blocksize ? blocksize : len; + memcpy(buffer, bh->b_data, cplen); + len -= cplen; + buffer += cplen; + + brelse(bh); + bh = NULL; + if (len == 0) + break; + } + cpos += num_clusters; + } +out: + return ret; +} + +static int ocfs2_xattr_ibody_get(struct inode *inode, + int name_index, + const char *name, + void *buffer, + size_t buffer_size, + struct ocfs2_xattr_search *xs) +{ + struct ocfs2_inode_info *oi = OCFS2_I(inode); + struct ocfs2_dinode *di = (struct ocfs2_dinode *)xs->inode_bh->b_data; + struct ocfs2_xattr_value_root *xv; + size_t size; + int ret = 0; + + if (!(oi->ip_dyn_features & OCFS2_INLINE_XATTR_FL)) + return -ENODATA; + + xs->end = (void *)di + inode->i_sb->s_blocksize; + xs->header = (struct ocfs2_xattr_header *) + (xs->end - le16_to_cpu(di->i_xattr_inline_size)); + xs->base = (void *)xs->header; + xs->here = xs->header->xh_entries; + + ret = ocfs2_xattr_find_entry(name_index, name, xs); + if (ret) + return ret; + size = le64_to_cpu(xs->here->xe_value_size); + if (buffer) { + if (size > buffer_size) + return -ERANGE; + if (ocfs2_xattr_is_local(xs->here)) { + memcpy(buffer, (void *)xs->base + + le16_to_cpu(xs->here->xe_name_offset) + + OCFS2_XATTR_SIZE(xs->here->xe_name_len), size); + } else { + xv = (struct ocfs2_xattr_value_root *) + (xs->base + le16_to_cpu( + xs->here->xe_name_offset) + + OCFS2_XATTR_SIZE(xs->here->xe_name_len)); + ret = ocfs2_xattr_get_value_outside(inode, xv, + buffer, size); + if (ret < 0) { + mlog_errno(ret); + return ret; + } + } + } + + return size; +} + +static int ocfs2_xattr_block_get(struct inode *inode, + int name_index, + const char *name, + void *buffer, + size_t buffer_size, + struct ocfs2_xattr_search *xs) +{ + struct ocfs2_dinode *di = (struct ocfs2_dinode *)xs->inode_bh->b_data; + struct buffer_head *blk_bh = NULL; + struct ocfs2_xattr_block *xb; + struct ocfs2_xattr_value_root *xv; + size_t size; + int ret = -ENODATA, name_offset, name_len, block_off, i; + + if (!di->i_xattr_loc) + return ret; + + memset(&xs->bucket, 0, sizeof(xs->bucket)); + + ret = ocfs2_read_block(inode, le64_to_cpu(di->i_xattr_loc), &blk_bh); + if (ret < 0) { + mlog_errno(ret); + return ret; + } + /*Verify the signature of xattr block*/ + if (memcmp((void *)blk_bh->b_data, OCFS2_XATTR_BLOCK_SIGNATURE, + strlen(OCFS2_XATTR_BLOCK_SIGNATURE))) { + ret = -EFAULT; + goto cleanup; + } + + xs->xattr_bh = blk_bh; + xb = (struct ocfs2_xattr_block *)blk_bh->b_data; + + if (!(le16_to_cpu(xb->xb_flags) & OCFS2_XATTR_INDEXED)) { + xs->header = &xb->xb_attrs.xb_header; + xs->base = (void *)xs->header; + xs->end = (void *)(blk_bh->b_data) + blk_bh->b_size; + xs->here = xs->header->xh_entries; + + ret = ocfs2_xattr_find_entry(name_index, name, xs); + } else + ret = ocfs2_xattr_index_block_find(inode, blk_bh, + name_index, + name, xs); + + if (ret) + goto cleanup; + size = le64_to_cpu(xs->here->xe_value_size); + if (buffer) { + ret = -ERANGE; + if (size > buffer_size) + goto cleanup; + + name_offset = le16_to_cpu(xs->here->xe_name_offset); + name_len = OCFS2_XATTR_SIZE(xs->here->xe_name_len); + i = xs->here - xs->header->xh_entries; + + if (le16_to_cpu(xb->xb_flags) & OCFS2_XATTR_INDEXED) { + ret = ocfs2_xattr_bucket_get_name_value(inode, + xs->bucket.xh, + i, + &block_off, + &name_offset); + xs->base = xs->bucket.bhs[block_off]->b_data; + } + if (ocfs2_xattr_is_local(xs->here)) { + memcpy(buffer, (void *)xs->base + + name_offset + name_len, size); + } else { + xv = (struct ocfs2_xattr_value_root *) + (xs->base + name_offset + name_len); + ret = ocfs2_xattr_get_value_outside(inode, xv, + buffer, size); + if (ret < 0) { + mlog_errno(ret); + goto cleanup; + } + } + } + ret = size; +cleanup: + for (i = 0; i < OCFS2_XATTR_MAX_BLOCKS_PER_BUCKET; i++) + brelse(xs->bucket.bhs[i]); + memset(&xs->bucket, 0, sizeof(xs->bucket)); + + brelse(blk_bh); + return ret; +} + +/* ocfs2_xattr_get() + * + * Copy an extended attribute into the buffer provided. + * Buffer is NULL to compute the size of buffer required. + */ +int ocfs2_xattr_get(struct inode *inode, + int name_index, + const char *name, + void *buffer, + size_t buffer_size) +{ + int ret; + struct ocfs2_dinode *di = NULL; + struct buffer_head *di_bh = NULL; + struct ocfs2_inode_info *oi = OCFS2_I(inode); + struct ocfs2_xattr_search xis = { + .not_found = -ENODATA, + }; + struct ocfs2_xattr_search xbs = { + .not_found = -ENODATA, + }; + + if (!ocfs2_supports_xattr(OCFS2_SB(inode->i_sb))) + return -EOPNOTSUPP; + + if (!(oi->ip_dyn_features & OCFS2_HAS_XATTR_FL)) + ret = -ENODATA; + + ret = ocfs2_inode_lock(inode, &di_bh, 0); + if (ret < 0) { + mlog_errno(ret); + return ret; + } + xis.inode_bh = xbs.inode_bh = di_bh; + di = (struct ocfs2_dinode *)di_bh->b_data; + + down_read(&oi->ip_xattr_sem); + ret = ocfs2_xattr_ibody_get(inode, name_index, name, buffer, + buffer_size, &xis); + if (ret == -ENODATA) + ret = ocfs2_xattr_block_get(inode, name_index, name, buffer, + buffer_size, &xbs); + up_read(&oi->ip_xattr_sem); + ocfs2_inode_unlock(inode, 0); + + brelse(di_bh); + + return ret; +} + +static int __ocfs2_xattr_set_value_outside(struct inode *inode, + struct ocfs2_xattr_value_root *xv, + const void *value, + int value_len) +{ + int ret = 0, i, cp_len, credits; + u16 blocksize = inode->i_sb->s_blocksize; + u32 p_cluster, num_clusters; + u32 cpos = 0, bpc = ocfs2_clusters_to_blocks(inode->i_sb, 1); + u32 clusters = ocfs2_clusters_for_bytes(inode->i_sb, value_len); + u64 blkno; + struct buffer_head *bh = NULL; + handle_t *handle; + + BUG_ON(clusters > le32_to_cpu(xv->xr_clusters)); + + credits = clusters * bpc; + handle = ocfs2_start_trans(OCFS2_SB(inode->i_sb), credits); + if (IS_ERR(handle)) { + ret = PTR_ERR(handle); + mlog_errno(ret); + goto out; + } + + while (cpos < clusters) { + ret = ocfs2_xattr_get_clusters(inode, cpos, &p_cluster, + &num_clusters, &xv->xr_list); + if (ret) { + mlog_errno(ret); + goto out_commit; + } + + blkno = ocfs2_clusters_to_blocks(inode->i_sb, p_cluster); + + for (i = 0; i < num_clusters * bpc; i++, blkno++) { + ret = ocfs2_read_block(inode, blkno, &bh); + if (ret) { + mlog_errno(ret); + goto out_commit; + } + + ret = ocfs2_journal_access(handle, + inode, + bh, + OCFS2_JOURNAL_ACCESS_WRITE); + if (ret < 0) { + mlog_errno(ret); + goto out_commit; + } + + cp_len = value_len > blocksize ? blocksize : value_len; + memcpy(bh->b_data, value, cp_len); + value_len -= cp_len; + value += cp_len; + if (cp_len < blocksize) + memset(bh->b_data + cp_len, 0, + blocksize - cp_len); + + ret = ocfs2_journal_dirty(handle, bh); + if (ret < 0) { + mlog_errno(ret); + goto out_commit; + } + brelse(bh); + bh = NULL; + + /* + * XXX: do we need to empty all the following + * blocks in this cluster? + */ + if (!value_len) + break; + } + cpos += num_clusters; + } +out_commit: + ocfs2_commit_trans(OCFS2_SB(inode->i_sb), handle); +out: + brelse(bh); + + return ret; +} + +static int ocfs2_xattr_cleanup(struct inode *inode, + struct ocfs2_xattr_info *xi, + struct ocfs2_xattr_search *xs, + size_t offs) +{ + handle_t *handle = NULL; + int ret = 0; + size_t name_len = strlen(xi->name); + void *val = xs->base + offs; + size_t size = OCFS2_XATTR_SIZE(name_len) + OCFS2_XATTR_ROOT_SIZE; + + handle = ocfs2_start_trans((OCFS2_SB(inode->i_sb)), + OCFS2_XATTR_BLOCK_UPDATE_CREDITS); + if (IS_ERR(handle)) { + ret = PTR_ERR(handle); + mlog_errno(ret); + goto out; + } + ret = ocfs2_journal_access(handle, inode, xs->xattr_bh, + OCFS2_JOURNAL_ACCESS_WRITE); + if (ret) { + mlog_errno(ret); + goto out_commit; + } + /* Decrease xattr count */ + le16_add_cpu(&xs->header->xh_count, -1); + /* Remove the xattr entry and tree root which has already be set*/ + memset((void *)xs->here, 0, sizeof(struct ocfs2_xattr_entry)); + memset(val, 0, size); + + ret = ocfs2_journal_dirty(handle, xs->xattr_bh); + if (ret < 0) + mlog_errno(ret); +out_commit: + ocfs2_commit_trans(OCFS2_SB(inode->i_sb), handle); +out: + return ret; +} + +static int ocfs2_xattr_update_entry(struct inode *inode, + struct ocfs2_xattr_info *xi, + struct ocfs2_xattr_search *xs, + size_t offs) +{ + handle_t *handle = NULL; + int ret = 0; + + handle = ocfs2_start_trans((OCFS2_SB(inode->i_sb)), + OCFS2_XATTR_BLOCK_UPDATE_CREDITS); + if (IS_ERR(handle)) { + ret = PTR_ERR(handle); + mlog_errno(ret); + goto out; + } + ret = ocfs2_journal_access(handle, inode, xs->xattr_bh, + OCFS2_JOURNAL_ACCESS_WRITE); + if (ret) { + mlog_errno(ret); + goto out_commit; + } + + xs->here->xe_name_offset = cpu_to_le16(offs); + xs->here->xe_value_size = cpu_to_le64(xi->value_len); + if (xi->value_len <= OCFS2_XATTR_INLINE_SIZE) + ocfs2_xattr_set_local(xs->here, 1); + else + ocfs2_xattr_set_local(xs->here, 0); + ocfs2_xattr_hash_entry(inode, xs->header, xs->here); + + ret = ocfs2_journal_dirty(handle, xs->xattr_bh); + if (ret < 0) + mlog_errno(ret); +out_commit: + ocfs2_commit_trans(OCFS2_SB(inode->i_sb), handle); +out: + return ret; +} + +/* + * ocfs2_xattr_set_value_outside() + * + * Set large size value in B tree. + */ +static int ocfs2_xattr_set_value_outside(struct inode *inode, + struct ocfs2_xattr_info *xi, + struct ocfs2_xattr_search *xs, + size_t offs) +{ + size_t name_len = strlen(xi->name); + void *val = xs->base + offs; + struct ocfs2_xattr_value_root *xv = NULL; + size_t size = OCFS2_XATTR_SIZE(name_len) + OCFS2_XATTR_ROOT_SIZE; + int ret = 0; + + memset(val, 0, size); + memcpy(val, xi->name, name_len); + xv = (struct ocfs2_xattr_value_root *) + (val + OCFS2_XATTR_SIZE(name_len)); + xv->xr_clusters = 0; + xv->xr_last_eb_blk = 0; + xv->xr_list.l_tree_depth = 0; + xv->xr_list.l_count = cpu_to_le16(1); + xv->xr_list.l_next_free_rec = 0; + + ret = ocfs2_xattr_value_truncate(inode, xs->xattr_bh, xv, + xi->value_len); + if (ret < 0) { + mlog_errno(ret); + return ret; + } + ret = __ocfs2_xattr_set_value_outside(inode, xv, xi->value, + xi->value_len); + if (ret < 0) { + mlog_errno(ret); + return ret; + } + ret = ocfs2_xattr_update_entry(inode, xi, xs, offs); + if (ret < 0) + mlog_errno(ret); + + return ret; +} + +/* + * ocfs2_xattr_set_entry_local() + * + * Set, replace or remove extended attribute in local. + */ +static void ocfs2_xattr_set_entry_local(struct inode *inode, + struct ocfs2_xattr_info *xi, + struct ocfs2_xattr_search *xs, + struct ocfs2_xattr_entry *last, + size_t min_offs) +{ + size_t name_len = strlen(xi->name); + int i; + + if (xi->value && xs->not_found) { + /* Insert the new xattr entry. */ + le16_add_cpu(&xs->header->xh_count, 1); + ocfs2_xattr_set_type(last, xi->name_index); + ocfs2_xattr_set_local(last, 1); + last->xe_name_len = name_len; + } else { + void *first_val; + void *val; + size_t offs, size; + + first_val = xs->base + min_offs; + offs = le16_to_cpu(xs->here->xe_name_offset); + val = xs->base + offs; + + if (le64_to_cpu(xs->here->xe_value_size) > + OCFS2_XATTR_INLINE_SIZE) + size = OCFS2_XATTR_SIZE(name_len) + + OCFS2_XATTR_ROOT_SIZE; + else + size = OCFS2_XATTR_SIZE(name_len) + + OCFS2_XATTR_SIZE(le64_to_cpu(xs->here->xe_value_size)); + + if (xi->value && size == OCFS2_XATTR_SIZE(name_len) + + OCFS2_XATTR_SIZE(xi->value_len)) { + /* The old and the new value have the + same size. Just replace the value. */ + ocfs2_xattr_set_local(xs->here, 1); + xs->here->xe_value_size = cpu_to_le64(xi->value_len); + /* Clear value bytes. */ + memset(val + OCFS2_XATTR_SIZE(name_len), + 0, + OCFS2_XATTR_SIZE(xi->value_len)); + memcpy(val + OCFS2_XATTR_SIZE(name_len), + xi->value, + xi->value_len); + return; + } + /* Remove the old name+value. */ + memmove(first_val + size, first_val, val - first_val); + memset(first_val, 0, size); + xs->here->xe_name_hash = 0; + xs->here->xe_name_offset = 0; + ocfs2_xattr_set_local(xs->here, 1); + xs->here->xe_value_size = 0; + + min_offs += size; + + /* Adjust all value offsets. */ + last = xs->header->xh_entries; + for (i = 0 ; i < le16_to_cpu(xs->header->xh_count); i++) { + size_t o = le16_to_cpu(last->xe_name_offset); + + if (o < offs) + last->xe_name_offset = cpu_to_le16(o + size); + last += 1; + } + + if (!xi->value) { + /* Remove the old entry. */ + last -= 1; + memmove(xs->here, xs->here + 1, + (void *)last - (void *)xs->here); + memset(last, 0, sizeof(struct ocfs2_xattr_entry)); + le16_add_cpu(&xs->header->xh_count, -1); + } + } + if (xi->value) { + /* Insert the new name+value. */ + size_t size = OCFS2_XATTR_SIZE(name_len) + + OCFS2_XATTR_SIZE(xi->value_len); + void *val = xs->base + min_offs - size; + + xs->here->xe_name_offset = cpu_to_le16(min_offs - size); + memset(val, 0, size); + memcpy(val, xi->name, name_len); + memcpy(val + OCFS2_XATTR_SIZE(name_len), + xi->value, + xi->value_len); + xs->here->xe_value_size = cpu_to_le64(xi->value_len); + ocfs2_xattr_set_local(xs->here, 1); + ocfs2_xattr_hash_entry(inode, xs->header, xs->here); + } + + return; +} + +/* + * ocfs2_xattr_set_entry() + * + * Set extended attribute entry into inode or block. + * + * If extended attribute value size > OCFS2_XATTR_INLINE_SIZE, + * We first insert tree root(ocfs2_xattr_value_root) with set_entry_local(), + * then set value in B tree with set_value_outside(). + */ +static int ocfs2_xattr_set_entry(struct inode *inode, + struct ocfs2_xattr_info *xi, + struct ocfs2_xattr_search *xs, + int flag) +{ + struct ocfs2_xattr_entry *last; + struct ocfs2_inode_info *oi = OCFS2_I(inode); + struct ocfs2_dinode *di = (struct ocfs2_dinode *)xs->inode_bh->b_data; + size_t min_offs = xs->end - xs->base, name_len = strlen(xi->name); + size_t size_l = 0; + handle_t *handle = NULL; + int free, i, ret; + struct ocfs2_xattr_info xi_l = { + .name_index = xi->name_index, + .name = xi->name, + .value = xi->value, + .value_len = xi->value_len, + }; + + /* Compute min_offs, last and free space. */ + last = xs->header->xh_entries; + + for (i = 0 ; i < le16_to_cpu(xs->header->xh_count); i++) { + size_t offs = le16_to_cpu(last->xe_name_offset); + if (offs < min_offs) + min_offs = offs; + last += 1; + } + + free = min_offs - ((void *)last - xs->base) - sizeof(__u32); + if (free < 0) + return -EFAULT; + + if (!xs->not_found) { + size_t size = 0; + if (ocfs2_xattr_is_local(xs->here)) + size = OCFS2_XATTR_SIZE(name_len) + + OCFS2_XATTR_SIZE(le64_to_cpu(xs->here->xe_value_size)); + else + size = OCFS2_XATTR_SIZE(name_len) + + OCFS2_XATTR_ROOT_SIZE; + free += (size + sizeof(struct ocfs2_xattr_entry)); + } + /* Check free space in inode or block */ + if (xi->value && xi->value_len > OCFS2_XATTR_INLINE_SIZE) { + if (free < sizeof(struct ocfs2_xattr_entry) + + OCFS2_XATTR_SIZE(name_len) + + OCFS2_XATTR_ROOT_SIZE) { + ret = -ENOSPC; + goto out; + } + size_l = OCFS2_XATTR_SIZE(name_len) + OCFS2_XATTR_ROOT_SIZE; + xi_l.value = (void *)&def_xv; + xi_l.value_len = OCFS2_XATTR_ROOT_SIZE; + } else if (xi->value) { + if (free < sizeof(struct ocfs2_xattr_entry) + + OCFS2_XATTR_SIZE(name_len) + + OCFS2_XATTR_SIZE(xi->value_len)) { + ret = -ENOSPC; + goto out; + } + } + + if (!xs->not_found) { + /* For existing extended attribute */ + size_t size = OCFS2_XATTR_SIZE(name_len) + + OCFS2_XATTR_SIZE(le64_to_cpu(xs->here->xe_value_size)); + size_t offs = le16_to_cpu(xs->here->xe_name_offset); + void *val = xs->base + offs; + + if (ocfs2_xattr_is_local(xs->here) && size == size_l) { + /* Replace existing local xattr with tree root */ + ret = ocfs2_xattr_set_value_outside(inode, xi, xs, + offs); + if (ret < 0) + mlog_errno(ret); + goto out; + } else if (!ocfs2_xattr_is_local(xs->here)) { + /* For existing xattr which has value outside */ + struct ocfs2_xattr_value_root *xv = NULL; + xv = (struct ocfs2_xattr_value_root *)(val + + OCFS2_XATTR_SIZE(name_len)); + + if (xi->value_len > OCFS2_XATTR_INLINE_SIZE) { + /* + * If new value need set outside also, + * first truncate old value to new value, + * then set new value with set_value_outside(). + */ + ret = ocfs2_xattr_value_truncate(inode, + xs->xattr_bh, + xv, + xi->value_len); + if (ret < 0) { + mlog_errno(ret); + goto out; + } + + ret = __ocfs2_xattr_set_value_outside(inode, + xv, + xi->value, + xi->value_len); + if (ret < 0) { + mlog_errno(ret); + goto out; + } + + ret = ocfs2_xattr_update_entry(inode, + xi, + xs, + offs); + if (ret < 0) + mlog_errno(ret); + goto out; + } else { + /* + * If new value need set in local, + * just trucate old value to zero. + */ + ret = ocfs2_xattr_value_truncate(inode, + xs->xattr_bh, + xv, + 0); + if (ret < 0) + mlog_errno(ret); + } + } + } + + handle = ocfs2_start_trans((OCFS2_SB(inode->i_sb)), + OCFS2_INODE_UPDATE_CREDITS); + if (IS_ERR(handle)) { + ret = PTR_ERR(handle); + mlog_errno(ret); + goto out; + } + + ret = ocfs2_journal_access(handle, inode, xs->inode_bh, + OCFS2_JOURNAL_ACCESS_WRITE); + if (ret) { + mlog_errno(ret); + goto out_commit; + } + + if (!(flag & OCFS2_INLINE_XATTR_FL)) { + /* set extended attribute in external block. */ + ret = ocfs2_extend_trans(handle, + OCFS2_INODE_UPDATE_CREDITS + + OCFS2_XATTR_BLOCK_UPDATE_CREDITS); + if (ret) { + mlog_errno(ret); + goto out_commit; + } + ret = ocfs2_journal_access(handle, inode, xs->xattr_bh, + OCFS2_JOURNAL_ACCESS_WRITE); + if (ret) { + mlog_errno(ret); + goto out_commit; + } + } + + /* + * Set value in local, include set tree root in local. + * This is the first step for value size >INLINE_SIZE. + */ + ocfs2_xattr_set_entry_local(inode, &xi_l, xs, last, min_offs); + + if (!(flag & OCFS2_INLINE_XATTR_FL)) { + ret = ocfs2_journal_dirty(handle, xs->xattr_bh); + if (ret < 0) { + mlog_errno(ret); + goto out_commit; + } + } + + if (!(oi->ip_dyn_features & OCFS2_INLINE_XATTR_FL) && + (flag & OCFS2_INLINE_XATTR_FL)) { + struct ocfs2_super *osb = OCFS2_SB(inode->i_sb); + unsigned int xattrsize = osb->s_xattr_inline_size; + + /* + * Adjust extent record count or inline data size + * to reserve space for extended attribute. + */ + if (oi->ip_dyn_features & OCFS2_INLINE_DATA_FL) { + struct ocfs2_inline_data *idata = &di->id2.i_data; + le16_add_cpu(&idata->id_count, -xattrsize); + } else if (!(ocfs2_inode_is_fast_symlink(inode))) { + struct ocfs2_extent_list *el = &di->id2.i_list; + le16_add_cpu(&el->l_count, -(xattrsize / + sizeof(struct ocfs2_extent_rec))); + } + di->i_xattr_inline_size = cpu_to_le16(xattrsize); + } + /* Update xattr flag */ + spin_lock(&oi->ip_lock); + oi->ip_dyn_features |= flag; + di->i_dyn_features = cpu_to_le16(oi->ip_dyn_features); + spin_unlock(&oi->ip_lock); + /* Update inode ctime */ + inode->i_ctime = CURRENT_TIME; + di->i_ctime = cpu_to_le64(inode->i_ctime.tv_sec); + di->i_ctime_nsec = cpu_to_le32(inode->i_ctime.tv_nsec); + + ret = ocfs2_journal_dirty(handle, xs->inode_bh); + if (ret < 0) + mlog_errno(ret); + +out_commit: + ocfs2_commit_trans(OCFS2_SB(inode->i_sb), handle); + + if (!ret && xi->value_len > OCFS2_XATTR_INLINE_SIZE) { + /* + * Set value outside in B tree. + * This is the second step for value size > INLINE_SIZE. + */ + size_t offs = le16_to_cpu(xs->here->xe_name_offset); + ret = ocfs2_xattr_set_value_outside(inode, xi, xs, offs); + if (ret < 0) { + int ret2; + + mlog_errno(ret); + /* + * If set value outside failed, we have to clean + * the junk tree root we have already set in local. + */ + ret2 = ocfs2_xattr_cleanup(inode, xi, xs, offs); + if (ret2 < 0) + mlog_errno(ret2); + } + } +out: + return ret; + +} + +static int ocfs2_remove_value_outside(struct inode*inode, + struct buffer_head *bh, + struct ocfs2_xattr_header *header) +{ + int ret = 0, i; + + for (i = 0; i < le16_to_cpu(header->xh_count); i++) { + struct ocfs2_xattr_entry *entry = &header->xh_entries[i]; + + if (!ocfs2_xattr_is_local(entry)) { + struct ocfs2_xattr_value_root *xv; + void *val; + + val = (void *)header + + le16_to_cpu(entry->xe_name_offset); + xv = (struct ocfs2_xattr_value_root *) + (val + OCFS2_XATTR_SIZE(entry->xe_name_len)); + ret = ocfs2_xattr_value_truncate(inode, bh, xv, 0); + if (ret < 0) { + mlog_errno(ret); + return ret; + } + } + } + + return ret; +} + +static int ocfs2_xattr_ibody_remove(struct inode *inode, + struct buffer_head *di_bh) +{ + + struct ocfs2_dinode *di = (struct ocfs2_dinode *)di_bh->b_data; + struct ocfs2_xattr_header *header; + int ret; + + header = (struct ocfs2_xattr_header *) + ((void *)di + inode->i_sb->s_blocksize - + le16_to_cpu(di->i_xattr_inline_size)); + + ret = ocfs2_remove_value_outside(inode, di_bh, header); + + return ret; +} + +static int ocfs2_xattr_block_remove(struct inode *inode, + struct buffer_head *blk_bh) +{ + struct ocfs2_xattr_block *xb; + int ret = 0; + + xb = (struct ocfs2_xattr_block *)blk_bh->b_data; + if (!(le16_to_cpu(xb->xb_flags) & OCFS2_XATTR_INDEXED)) { + struct ocfs2_xattr_header *header = &(xb->xb_attrs.xb_header); + ret = ocfs2_remove_value_outside(inode, blk_bh, header); + } else + ret = ocfs2_delete_xattr_index_block(inode, blk_bh); + + return ret; +} + +static int ocfs2_xattr_free_block(struct inode *inode, + u64 block) +{ + struct inode *xb_alloc_inode; + struct buffer_head *xb_alloc_bh = NULL; + struct buffer_head *blk_bh = NULL; + struct ocfs2_xattr_block *xb; + struct ocfs2_super *osb = OCFS2_SB(inode->i_sb); + handle_t *handle; + int ret = 0; + u64 blk, bg_blkno; + u16 bit; + + ret = ocfs2_read_block(inode, block, &blk_bh); + if (ret < 0) { + mlog_errno(ret); + goto out; + } + + /*Verify the signature of xattr block*/ + if (memcmp((void *)blk_bh->b_data, OCFS2_XATTR_BLOCK_SIGNATURE, + strlen(OCFS2_XATTR_BLOCK_SIGNATURE))) { + ret = -EFAULT; + goto out; + } + + ret = ocfs2_xattr_block_remove(inode, blk_bh); + if (ret < 0) { + mlog_errno(ret); + goto out; + } + + xb = (struct ocfs2_xattr_block *)blk_bh->b_data; + blk = le64_to_cpu(xb->xb_blkno); + bit = le16_to_cpu(xb->xb_suballoc_bit); + bg_blkno = ocfs2_which_suballoc_group(blk, bit); + + xb_alloc_inode = ocfs2_get_system_file_inode(osb, + EXTENT_ALLOC_SYSTEM_INODE, + le16_to_cpu(xb->xb_suballoc_slot)); + if (!xb_alloc_inode) { + ret = -ENOMEM; + mlog_errno(ret); + goto out; + } + mutex_lock(&xb_alloc_inode->i_mutex); + + ret = ocfs2_inode_lock(xb_alloc_inode, &xb_alloc_bh, 1); + if (ret < 0) { + mlog_errno(ret); + goto out_mutex; + } + + handle = ocfs2_start_trans(osb, OCFS2_SUBALLOC_FREE); + if (IS_ERR(handle)) { + ret = PTR_ERR(handle); + mlog_errno(ret); + goto out_unlock; + } + + ret = ocfs2_free_suballoc_bits(handle, xb_alloc_inode, xb_alloc_bh, + bit, bg_blkno, 1); + if (ret < 0) + mlog_errno(ret); + + ocfs2_commit_trans(osb, handle); +out_unlock: + ocfs2_inode_unlock(xb_alloc_inode, 1); + brelse(xb_alloc_bh); +out_mutex: + mutex_unlock(&xb_alloc_inode->i_mutex); + iput(xb_alloc_inode); +out: + brelse(blk_bh); + return ret; +} + +/* + * ocfs2_xattr_remove() + * + * Free extended attribute resources associated with this inode. + */ +int ocfs2_xattr_remove(struct inode *inode, struct buffer_head *di_bh) +{ + struct ocfs2_inode_info *oi = OCFS2_I(inode); + struct ocfs2_dinode *di = (struct ocfs2_dinode *)di_bh->b_data; + handle_t *handle; + int ret; + + if (!ocfs2_supports_xattr(OCFS2_SB(inode->i_sb))) + return 0; + + if (!(oi->ip_dyn_features & OCFS2_HAS_XATTR_FL)) + return 0; + + if (oi->ip_dyn_features & OCFS2_INLINE_XATTR_FL) { + ret = ocfs2_xattr_ibody_remove(inode, di_bh); + if (ret < 0) { + mlog_errno(ret); + goto out; + } + } + + if (di->i_xattr_loc) { + ret = ocfs2_xattr_free_block(inode, + le64_to_cpu(di->i_xattr_loc)); + if (ret < 0) { + mlog_errno(ret); + goto out; + } + } + + handle = ocfs2_start_trans((OCFS2_SB(inode->i_sb)), + OCFS2_INODE_UPDATE_CREDITS); + if (IS_ERR(handle)) { + ret = PTR_ERR(handle); + mlog_errno(ret); + goto out; + } + ret = ocfs2_journal_access(handle, inode, di_bh, + OCFS2_JOURNAL_ACCESS_WRITE); + if (ret) { + mlog_errno(ret); + goto out_commit; + } + + di->i_xattr_loc = 0; + + spin_lock(&oi->ip_lock); + oi->ip_dyn_features &= ~(OCFS2_INLINE_XATTR_FL | OCFS2_HAS_XATTR_FL); + di->i_dyn_features = cpu_to_le16(oi->ip_dyn_features); + spin_unlock(&oi->ip_lock); + + ret = ocfs2_journal_dirty(handle, di_bh); + if (ret < 0) + mlog_errno(ret); +out_commit: + ocfs2_commit_trans(OCFS2_SB(inode->i_sb), handle); +out: + return ret; +} + +static int ocfs2_xattr_has_space_inline(struct inode *inode, + struct ocfs2_dinode *di) +{ + struct ocfs2_inode_info *oi = OCFS2_I(inode); + unsigned int xattrsize = OCFS2_SB(inode->i_sb)->s_xattr_inline_size; + int free; + + if (xattrsize < OCFS2_MIN_XATTR_INLINE_SIZE) + return 0; + + if (oi->ip_dyn_features & OCFS2_INLINE_DATA_FL) { + struct ocfs2_inline_data *idata = &di->id2.i_data; + free = le16_to_cpu(idata->id_count) - le64_to_cpu(di->i_size); + } else if (ocfs2_inode_is_fast_symlink(inode)) { + free = ocfs2_fast_symlink_chars(inode->i_sb) - + le64_to_cpu(di->i_size); + } else { + struct ocfs2_extent_list *el = &di->id2.i_list; + free = (le16_to_cpu(el->l_count) - + le16_to_cpu(el->l_next_free_rec)) * + sizeof(struct ocfs2_extent_rec); + } + if (free >= xattrsize) + return 1; + + return 0; +} + +/* + * ocfs2_xattr_ibody_find() + * + * Find extended attribute in inode block and + * fill search info into struct ocfs2_xattr_search. + */ +static int ocfs2_xattr_ibody_find(struct inode *inode, + int name_index, + const char *name, + struct ocfs2_xattr_search *xs) +{ + struct ocfs2_inode_info *oi = OCFS2_I(inode); + struct ocfs2_dinode *di = (struct ocfs2_dinode *)xs->inode_bh->b_data; + int ret; + int has_space = 0; + + if (inode->i_sb->s_blocksize == OCFS2_MIN_BLOCKSIZE) + return 0; + + if (!(oi->ip_dyn_features & OCFS2_INLINE_XATTR_FL)) { + down_read(&oi->ip_alloc_sem); + has_space = ocfs2_xattr_has_space_inline(inode, di); + up_read(&oi->ip_alloc_sem); + if (!has_space) + return 0; + } + + xs->xattr_bh = xs->inode_bh; + xs->end = (void *)di + inode->i_sb->s_blocksize; + if (oi->ip_dyn_features & OCFS2_INLINE_XATTR_FL) + xs->header = (struct ocfs2_xattr_header *) + (xs->end - le16_to_cpu(di->i_xattr_inline_size)); + else + xs->header = (struct ocfs2_xattr_header *) + (xs->end - OCFS2_SB(inode->i_sb)->s_xattr_inline_size); + xs->base = (void *)xs->header; + xs->here = xs->header->xh_entries; + + /* Find the named attribute. */ + if (oi->ip_dyn_features & OCFS2_INLINE_XATTR_FL) { + ret = ocfs2_xattr_find_entry(name_index, name, xs); + if (ret && ret != -ENODATA) + return ret; + xs->not_found = ret; + } + + return 0; +} + +/* + * ocfs2_xattr_ibody_set() + * + * Set, replace or remove an extended attribute into inode block. + * + */ +static int ocfs2_xattr_ibody_set(struct inode *inode, + struct ocfs2_xattr_info *xi, + struct ocfs2_xattr_search *xs) +{ + struct ocfs2_inode_info *oi = OCFS2_I(inode); + struct ocfs2_dinode *di = (struct ocfs2_dinode *)xs->inode_bh->b_data; + int ret; + + if (inode->i_sb->s_blocksize == OCFS2_MIN_BLOCKSIZE) + return -ENOSPC; + + down_write(&oi->ip_alloc_sem); + if (!(oi->ip_dyn_features & OCFS2_INLINE_XATTR_FL)) { + if (!ocfs2_xattr_has_space_inline(inode, di)) { + ret = -ENOSPC; + goto out; + } + } + + ret = ocfs2_xattr_set_entry(inode, xi, xs, + (OCFS2_INLINE_XATTR_FL | OCFS2_HAS_XATTR_FL)); +out: + up_write(&oi->ip_alloc_sem); + + return ret; +} + +/* + * ocfs2_xattr_block_find() + * + * Find extended attribute in external block and + * fill search info into struct ocfs2_xattr_search. + */ +static int ocfs2_xattr_block_find(struct inode *inode, + int name_index, + const char *name, + struct ocfs2_xattr_search *xs) +{ + struct ocfs2_dinode *di = (struct ocfs2_dinode *)xs->inode_bh->b_data; + struct buffer_head *blk_bh = NULL; + struct ocfs2_xattr_block *xb; + int ret = 0; + + if (!di->i_xattr_loc) + return ret; + + ret = ocfs2_read_block(inode, le64_to_cpu(di->i_xattr_loc), &blk_bh); + if (ret < 0) { + mlog_errno(ret); + return ret; + } + /*Verify the signature of xattr block*/ + if (memcmp((void *)blk_bh->b_data, OCFS2_XATTR_BLOCK_SIGNATURE, + strlen(OCFS2_XATTR_BLOCK_SIGNATURE))) { + ret = -EFAULT; + goto cleanup; + } + + xs->xattr_bh = blk_bh; + xb = (struct ocfs2_xattr_block *)blk_bh->b_data; + + if (!(le16_to_cpu(xb->xb_flags) & OCFS2_XATTR_INDEXED)) { + xs->header = &xb->xb_attrs.xb_header; + xs->base = (void *)xs->header; + xs->end = (void *)(blk_bh->b_data) + blk_bh->b_size; + xs->here = xs->header->xh_entries; + + ret = ocfs2_xattr_find_entry(name_index, name, xs); + } else + ret = ocfs2_xattr_index_block_find(inode, blk_bh, + name_index, + name, xs); + + if (ret && ret != -ENODATA) { + xs->xattr_bh = NULL; + goto cleanup; + } + xs->not_found = ret; + return 0; +cleanup: + brelse(blk_bh); + + return ret; +} + +/* + * When all the xattrs are deleted from index btree, the ocfs2_xattr_tree + * will be erased and ocfs2_xattr_block will have its ocfs2_xattr_header + * re-initialized. + */ +static int ocfs2_restore_xattr_block(struct inode *inode, + struct ocfs2_xattr_search *xs) +{ + int ret; + handle_t *handle; + struct ocfs2_super *osb = OCFS2_SB(inode->i_sb); + struct ocfs2_xattr_block *xb = + (struct ocfs2_xattr_block *)xs->xattr_bh->b_data; + struct ocfs2_extent_list *el = &xb->xb_attrs.xb_root.xt_list; + u16 xb_flags = le16_to_cpu(xb->xb_flags); + + BUG_ON(!(xb_flags & OCFS2_XATTR_INDEXED) || + le16_to_cpu(el->l_next_free_rec) != 0); + + handle = ocfs2_start_trans(osb, OCFS2_XATTR_BLOCK_UPDATE_CREDITS); + if (IS_ERR(handle)) { + ret = PTR_ERR(handle); + handle = NULL; + goto out; + } + + ret = ocfs2_journal_access(handle, inode, xs->xattr_bh, + OCFS2_JOURNAL_ACCESS_WRITE); + if (ret < 0) { + mlog_errno(ret); + goto out_commit; + } + + memset(&xb->xb_attrs, 0, inode->i_sb->s_blocksize - + offsetof(struct ocfs2_xattr_block, xb_attrs)); + + xb->xb_flags = cpu_to_le16(xb_flags & ~OCFS2_XATTR_INDEXED); + + ocfs2_journal_dirty(handle, xs->xattr_bh); + +out_commit: + ocfs2_commit_trans(osb, handle); +out: + return ret; +} + +/* + * ocfs2_xattr_block_set() + * + * Set, replace or remove an extended attribute into external block. + * + */ +static int ocfs2_xattr_block_set(struct inode *inode, + struct ocfs2_xattr_info *xi, + struct ocfs2_xattr_search *xs) +{ + struct buffer_head *new_bh = NULL; + struct ocfs2_super *osb = OCFS2_SB(inode->i_sb); + struct ocfs2_dinode *di = (struct ocfs2_dinode *)xs->inode_bh->b_data; + struct ocfs2_alloc_context *meta_ac = NULL; + handle_t *handle = NULL; + struct ocfs2_xattr_block *xblk = NULL; + u16 suballoc_bit_start; + u32 num_got; + u64 first_blkno; + int ret; + + if (!xs->xattr_bh) { + /* + * Alloc one external block for extended attribute + * outside of inode. + */ + ret = ocfs2_reserve_new_metadata_blocks(osb, 1, &meta_ac); + if (ret < 0) { + mlog_errno(ret); + goto out; + } + handle = ocfs2_start_trans(osb, + OCFS2_XATTR_BLOCK_CREATE_CREDITS); + if (IS_ERR(handle)) { + ret = PTR_ERR(handle); + mlog_errno(ret); + goto out; + } + ret = ocfs2_journal_access(handle, inode, xs->inode_bh, + OCFS2_JOURNAL_ACCESS_CREATE); + if (ret < 0) { + mlog_errno(ret); + goto out_commit; + } + + ret = ocfs2_claim_metadata(osb, handle, meta_ac, 1, + &suballoc_bit_start, &num_got, + &first_blkno); + if (ret < 0) { + mlog_errno(ret); + goto out_commit; + } + + new_bh = sb_getblk(inode->i_sb, first_blkno); + ocfs2_set_new_buffer_uptodate(inode, new_bh); + + ret = ocfs2_journal_access(handle, inode, new_bh, + OCFS2_JOURNAL_ACCESS_CREATE); + if (ret < 0) { + mlog_errno(ret); + goto out_commit; + } + + /* Initialize ocfs2_xattr_block */ + xs->xattr_bh = new_bh; + xblk = (struct ocfs2_xattr_block *)new_bh->b_data; + memset(xblk, 0, inode->i_sb->s_blocksize); + strcpy((void *)xblk, OCFS2_XATTR_BLOCK_SIGNATURE); + xblk->xb_suballoc_slot = cpu_to_le16(osb->slot_num); + xblk->xb_suballoc_bit = cpu_to_le16(suballoc_bit_start); + xblk->xb_fs_generation = cpu_to_le32(osb->fs_generation); + xblk->xb_blkno = cpu_to_le64(first_blkno); + + xs->header = &xblk->xb_attrs.xb_header; + xs->base = (void *)xs->header; + xs->end = (void *)xblk + inode->i_sb->s_blocksize; + xs->here = xs->header->xh_entries; + + + ret = ocfs2_journal_dirty(handle, new_bh); + if (ret < 0) { + mlog_errno(ret); + goto out_commit; + } + di->i_xattr_loc = cpu_to_le64(first_blkno); + ret = ocfs2_journal_dirty(handle, xs->inode_bh); + if (ret < 0) + mlog_errno(ret); +out_commit: + ocfs2_commit_trans(osb, handle); +out: + if (meta_ac) + ocfs2_free_alloc_context(meta_ac); + if (ret < 0) + return ret; + } else + xblk = (struct ocfs2_xattr_block *)xs->xattr_bh->b_data; + + if (!(le16_to_cpu(xblk->xb_flags) & OCFS2_XATTR_INDEXED)) { + /* Set extended attribute into external block */ + ret = ocfs2_xattr_set_entry(inode, xi, xs, OCFS2_HAS_XATTR_FL); + if (!ret || ret != -ENOSPC) + goto end; + + ret = ocfs2_xattr_create_index_block(inode, xs); + if (ret) + goto end; + } + + ret = ocfs2_xattr_set_entry_index_block(inode, xi, xs); + if (!ret && xblk->xb_attrs.xb_root.xt_list.l_next_free_rec == 0) + ret = ocfs2_restore_xattr_block(inode, xs); + +end: + + return ret; +} + +/* + * ocfs2_xattr_set() + * + * Set, replace or remove an extended attribute for this inode. + * value is NULL to remove an existing extended attribute, else either + * create or replace an extended attribute. + */ +int ocfs2_xattr_set(struct inode *inode, + int name_index, + const char *name, + const void *value, + size_t value_len, + int flags) +{ + struct buffer_head *di_bh = NULL; + struct ocfs2_dinode *di; + int ret; + u16 i, blk_per_bucket = ocfs2_blocks_per_xattr_bucket(inode->i_sb); + + struct ocfs2_xattr_info xi = { + .name_index = name_index, + .name = name, + .value = value, + .value_len = value_len, + }; + + struct ocfs2_xattr_search xis = { + .not_found = -ENODATA, + }; + + struct ocfs2_xattr_search xbs = { + .not_found = -ENODATA, + }; + + if (!ocfs2_supports_xattr(OCFS2_SB(inode->i_sb))) + return -EOPNOTSUPP; + + ret = ocfs2_inode_lock(inode, &di_bh, 1); + if (ret < 0) { + mlog_errno(ret); + return ret; + } + xis.inode_bh = xbs.inode_bh = di_bh; + di = (struct ocfs2_dinode *)di_bh->b_data; + + down_write(&OCFS2_I(inode)->ip_xattr_sem); + /* + * Scan inode and external block to find the same name + * extended attribute and collect search infomation. + */ + ret = ocfs2_xattr_ibody_find(inode, name_index, name, &xis); + if (ret) + goto cleanup; + if (xis.not_found) { + ret = ocfs2_xattr_block_find(inode, name_index, name, &xbs); + if (ret) + goto cleanup; + } + + if (xis.not_found && xbs.not_found) { + ret = -ENODATA; + if (flags & XATTR_REPLACE) + goto cleanup; + ret = 0; + if (!value) + goto cleanup; + } else { + ret = -EEXIST; + if (flags & XATTR_CREATE) + goto cleanup; + } + + if (!value) { + /* Remove existing extended attribute */ + if (!xis.not_found) + ret = ocfs2_xattr_ibody_set(inode, &xi, &xis); + else if (!xbs.not_found) + ret = ocfs2_xattr_block_set(inode, &xi, &xbs); + } else { + /* We always try to set extended attribute into inode first*/ + ret = ocfs2_xattr_ibody_set(inode, &xi, &xis); + if (!ret && !xbs.not_found) { + /* + * If succeed and that extended attribute existing in + * external block, then we will remove it. + */ + xi.value = NULL; + xi.value_len = 0; + ret = ocfs2_xattr_block_set(inode, &xi, &xbs); + } else if (ret == -ENOSPC) { + if (di->i_xattr_loc && !xbs.xattr_bh) { + ret = ocfs2_xattr_block_find(inode, name_index, + name, &xbs); + if (ret) + goto cleanup; + } + /* + * If no space in inode, we will set extended attribute + * into external block. + */ + ret = ocfs2_xattr_block_set(inode, &xi, &xbs); + if (ret) + goto cleanup; + if (!xis.not_found) { + /* + * If succeed and that extended attribute + * existing in inode, we will remove it. + */ + xi.value = NULL; + xi.value_len = 0; + ret = ocfs2_xattr_ibody_set(inode, &xi, &xis); + } + } + } +cleanup: + up_write(&OCFS2_I(inode)->ip_xattr_sem); + ocfs2_inode_unlock(inode, 1); + brelse(di_bh); + brelse(xbs.xattr_bh); + for (i = 0; i < blk_per_bucket; i++) + brelse(xbs.bucket.bhs[i]); + + return ret; +} + +/* + * Find the xattr extent rec which may contains name_hash. + * e_cpos will be the first name hash of the xattr rec. + * el must be the ocfs2_xattr_header.xb_attrs.xb_root.xt_list. + */ +static int ocfs2_xattr_get_rec(struct inode *inode, + u32 name_hash, + u64 *p_blkno, + u32 *e_cpos, + u32 *num_clusters, + struct ocfs2_extent_list *el) +{ + int ret = 0, i; + struct buffer_head *eb_bh = NULL; + struct ocfs2_extent_block *eb; + struct ocfs2_extent_rec *rec = NULL; + u64 e_blkno = 0; + + if (el->l_tree_depth) { + ret = ocfs2_find_leaf(inode, el, name_hash, &eb_bh); + if (ret) { + mlog_errno(ret); + goto out; + } + + eb = (struct ocfs2_extent_block *) eb_bh->b_data; + el = &eb->h_list; + + if (el->l_tree_depth) { + ocfs2_error(inode->i_sb, + "Inode %lu has non zero tree depth in " + "xattr tree block %llu\n", inode->i_ino, + (unsigned long long)eb_bh->b_blocknr); + ret = -EROFS; + goto out; + } + } + + for (i = le16_to_cpu(el->l_next_free_rec) - 1; i >= 0; i--) { + rec = &el->l_recs[i]; + + if (le32_to_cpu(rec->e_cpos) <= name_hash) { + e_blkno = le64_to_cpu(rec->e_blkno); + break; + } + } + + if (!e_blkno) { + ocfs2_error(inode->i_sb, "Inode %lu has bad extent " + "record (%u, %u, 0) in xattr", inode->i_ino, + le32_to_cpu(rec->e_cpos), + ocfs2_rec_clusters(el, rec)); + ret = -EROFS; + goto out; + } + + *p_blkno = le64_to_cpu(rec->e_blkno); + *num_clusters = le16_to_cpu(rec->e_leaf_clusters); + if (e_cpos) + *e_cpos = le32_to_cpu(rec->e_cpos); +out: + brelse(eb_bh); + return ret; +} + +typedef int (xattr_bucket_func)(struct inode *inode, + struct ocfs2_xattr_bucket *bucket, + void *para); + +static int ocfs2_find_xe_in_bucket(struct inode *inode, + struct buffer_head *header_bh, + int name_index, + const char *name, + u32 name_hash, + u16 *xe_index, + int *found) +{ + int i, ret = 0, cmp = 1, block_off, new_offset; + struct ocfs2_xattr_header *xh = + (struct ocfs2_xattr_header *)header_bh->b_data; + size_t name_len = strlen(name); + struct ocfs2_xattr_entry *xe = NULL; + struct buffer_head *name_bh = NULL; + char *xe_name; + + /* + * We don't use binary search in the bucket because there + * may be multiple entries with the same name hash. + */ + for (i = 0; i < le16_to_cpu(xh->xh_count); i++) { + xe = &xh->xh_entries[i]; + + if (name_hash > le32_to_cpu(xe->xe_name_hash)) + continue; + else if (name_hash < le32_to_cpu(xe->xe_name_hash)) + break; + + cmp = name_index - ocfs2_xattr_get_type(xe); + if (!cmp) + cmp = name_len - xe->xe_name_len; + if (cmp) + continue; + + ret = ocfs2_xattr_bucket_get_name_value(inode, + xh, + i, + &block_off, + &new_offset); + if (ret) { + mlog_errno(ret); + break; + } + + ret = ocfs2_read_block(inode, header_bh->b_blocknr + block_off, + &name_bh); + if (ret) { + mlog_errno(ret); + break; + } + xe_name = name_bh->b_data + new_offset; + + cmp = memcmp(name, xe_name, name_len); + brelse(name_bh); + name_bh = NULL; + + if (cmp == 0) { + *xe_index = i; + *found = 1; + ret = 0; + break; + } + } + + return ret; +} + +/* + * Find the specified xattr entry in a series of buckets. + * This series start from p_blkno and last for num_clusters. + * The ocfs2_xattr_header.xh_num_buckets of the first bucket contains + * the num of the valid buckets. + * + * Return the buffer_head this xattr should reside in. And if the xattr's + * hash is in the gap of 2 buckets, return the lower bucket. + */ +static int ocfs2_xattr_bucket_find(struct inode *inode, + int name_index, + const char *name, + u32 name_hash, + u64 p_blkno, + u32 first_hash, + u32 num_clusters, + struct ocfs2_xattr_search *xs) +{ + int ret, found = 0; + struct buffer_head *bh = NULL; + struct buffer_head *lower_bh = NULL; + struct ocfs2_xattr_header *xh = NULL; + struct ocfs2_xattr_entry *xe = NULL; + u16 index = 0; + u16 blk_per_bucket = ocfs2_blocks_per_xattr_bucket(inode->i_sb); + int low_bucket = 0, bucket, high_bucket; + u32 last_hash; + u64 blkno; + + ret = ocfs2_read_block(inode, p_blkno, &bh); + if (ret) { + mlog_errno(ret); + goto out; + } + + xh = (struct ocfs2_xattr_header *)bh->b_data; + high_bucket = le16_to_cpu(xh->xh_num_buckets) - 1; + + while (low_bucket <= high_bucket) { + brelse(bh); + bh = NULL; + bucket = (low_bucket + high_bucket) / 2; + + blkno = p_blkno + bucket * blk_per_bucket; + + ret = ocfs2_read_block(inode, blkno, &bh); + if (ret) { + mlog_errno(ret); + goto out; + } + + xh = (struct ocfs2_xattr_header *)bh->b_data; + xe = &xh->xh_entries[0]; + if (name_hash < le32_to_cpu(xe->xe_name_hash)) { + high_bucket = bucket - 1; + continue; + } + + /* + * Check whether the hash of the last entry in our + * bucket is larger than the search one. for an empty + * bucket, the last one is also the first one. + */ + if (xh->xh_count) + xe = &xh->xh_entries[le16_to_cpu(xh->xh_count) - 1]; + + last_hash = le32_to_cpu(xe->xe_name_hash); + + /* record lower_bh which may be the insert place. */ + brelse(lower_bh); + lower_bh = bh; + bh = NULL; + + if (name_hash > le32_to_cpu(xe->xe_name_hash)) { + low_bucket = bucket + 1; + continue; + } + + /* the searched xattr should reside in this bucket if exists. */ + ret = ocfs2_find_xe_in_bucket(inode, lower_bh, + name_index, name, name_hash, + &index, &found); + if (ret) { + mlog_errno(ret); + goto out; + } + break; + } + + /* + * Record the bucket we have found. + * When the xattr's hash value is in the gap of 2 buckets, we will + * always set it to the previous bucket. + */ + if (!lower_bh) { + /* + * We can't find any bucket whose first name_hash is less + * than the find name_hash. + */ + BUG_ON(bh->b_blocknr != p_blkno); + lower_bh = bh; + bh = NULL; + } + xs->bucket.bhs[0] = lower_bh; + xs->bucket.xh = (struct ocfs2_xattr_header *) + xs->bucket.bhs[0]->b_data; + lower_bh = NULL; + + xs->header = xs->bucket.xh; + xs->base = xs->bucket.bhs[0]->b_data; + xs->end = xs->base + inode->i_sb->s_blocksize; + + if (found) { + /* + * If we have found the xattr enty, read all the blocks in + * this bucket. + */ + ret = ocfs2_read_blocks(inode, xs->bucket.bhs[0]->b_blocknr + 1, + blk_per_bucket - 1, &xs->bucket.bhs[1], + OCFS2_BH_CACHED); + if (ret) { + mlog_errno(ret); + goto out; + } + + xs->here = &xs->header->xh_entries[index]; + mlog(0, "find xattr %s in bucket %llu, entry = %u\n", name, + (unsigned long long)xs->bucket.bhs[0]->b_blocknr, index); + } else + ret = -ENODATA; + +out: + brelse(bh); + brelse(lower_bh); + return ret; +} + +static int ocfs2_xattr_index_block_find(struct inode *inode, + struct buffer_head *root_bh, + int name_index, + const char *name, + struct ocfs2_xattr_search *xs) +{ + int ret; + struct ocfs2_xattr_block *xb = + (struct ocfs2_xattr_block *)root_bh->b_data; + struct ocfs2_xattr_tree_root *xb_root = &xb->xb_attrs.xb_root; + struct ocfs2_extent_list *el = &xb_root->xt_list; + u64 p_blkno = 0; + u32 first_hash, num_clusters = 0; + u32 name_hash = ocfs2_xattr_name_hash(inode, name, strlen(name)); + + if (le16_to_cpu(el->l_next_free_rec) == 0) + return -ENODATA; + + mlog(0, "find xattr %s, hash = %u, index = %d in xattr tree\n", + name, name_hash, name_index); + + ret = ocfs2_xattr_get_rec(inode, name_hash, &p_blkno, &first_hash, + &num_clusters, el); + if (ret) { + mlog_errno(ret); + goto out; + } + + BUG_ON(p_blkno == 0 || num_clusters == 0 || first_hash > name_hash); + + mlog(0, "find xattr extent rec %u clusters from %llu, the first hash " + "in the rec is %u\n", num_clusters, p_blkno, first_hash); + + ret = ocfs2_xattr_bucket_find(inode, name_index, name, name_hash, + p_blkno, first_hash, num_clusters, xs); + +out: + return ret; +} + +static int ocfs2_iterate_xattr_buckets(struct inode *inode, + u64 blkno, + u32 clusters, + xattr_bucket_func *func, + void *para) +{ + int i, j, ret = 0; + int blk_per_bucket = ocfs2_blocks_per_xattr_bucket(inode->i_sb); + u32 bpc = ocfs2_xattr_buckets_per_cluster(OCFS2_SB(inode->i_sb)); + u32 num_buckets = clusters * bpc; + struct ocfs2_xattr_bucket bucket; + + memset(&bucket, 0, sizeof(bucket)); + + mlog(0, "iterating xattr buckets in %u clusters starting from %llu\n", + clusters, blkno); + + for (i = 0; i < num_buckets; i++, blkno += blk_per_bucket) { + ret = ocfs2_read_blocks(inode, blkno, blk_per_bucket, + bucket.bhs, OCFS2_BH_CACHED); + if (ret) { + mlog_errno(ret); + goto out; + } + + bucket.xh = (struct ocfs2_xattr_header *)bucket.bhs[0]->b_data; + /* + * The real bucket num in this series of blocks is stored + * in the 1st bucket. + */ + if (i == 0) + num_buckets = le16_to_cpu(bucket.xh->xh_num_buckets); + + mlog(0, "iterating xattr bucket %llu, first hash %u\n", blkno, + le32_to_cpu(bucket.xh->xh_entries[0].xe_name_hash)); + if (func) { + ret = func(inode, &bucket, para); + if (ret) { + mlog_errno(ret); + break; + } + } + + for (j = 0; j < blk_per_bucket; j++) + brelse(bucket.bhs[j]); + memset(&bucket, 0, sizeof(bucket)); + } + +out: + for (j = 0; j < blk_per_bucket; j++) + brelse(bucket.bhs[j]); + + return ret; +} + +struct ocfs2_xattr_tree_list { + char *buffer; + size_t buffer_size; + size_t result; +}; + +static int ocfs2_xattr_bucket_get_name_value(struct inode *inode, + struct ocfs2_xattr_header *xh, + int index, + int *block_off, + int *new_offset) +{ + u16 name_offset; + + if (index < 0 || index >= le16_to_cpu(xh->xh_count)) + return -EINVAL; + + name_offset = le16_to_cpu(xh->xh_entries[index].xe_name_offset); + + *block_off = name_offset >> inode->i_sb->s_blocksize_bits; + *new_offset = name_offset % inode->i_sb->s_blocksize; + + return 0; +} + +static int ocfs2_list_xattr_bucket(struct inode *inode, + struct ocfs2_xattr_bucket *bucket, + void *para) +{ + int ret = 0, type; + struct ocfs2_xattr_tree_list *xl = (struct ocfs2_xattr_tree_list *)para; + int i, block_off, new_offset; + const char *prefix, *name; + + for (i = 0 ; i < le16_to_cpu(bucket->xh->xh_count); i++) { + struct ocfs2_xattr_entry *entry = &bucket->xh->xh_entries[i]; + type = ocfs2_xattr_get_type(entry); + prefix = ocfs2_xattr_prefix(type); + + if (prefix) { + ret = ocfs2_xattr_bucket_get_name_value(inode, + bucket->xh, + i, + &block_off, + &new_offset); + if (ret) + break; + + name = (const char *)bucket->bhs[block_off]->b_data + + new_offset; + ret = ocfs2_xattr_list_entry(xl->buffer, + xl->buffer_size, + &xl->result, + prefix, name, + entry->xe_name_len); + if (ret) + break; + } + } + + return ret; +} + +static int ocfs2_xattr_tree_list_index_block(struct inode *inode, + struct ocfs2_xattr_tree_root *xt, + char *buffer, + size_t buffer_size) +{ + struct ocfs2_extent_list *el = &xt->xt_list; + int ret = 0; + u32 name_hash = UINT_MAX, e_cpos = 0, num_clusters = 0; + u64 p_blkno = 0; + struct ocfs2_xattr_tree_list xl = { + .buffer = buffer, + .buffer_size = buffer_size, + .result = 0, + }; + + if (le16_to_cpu(el->l_next_free_rec) == 0) + return 0; + + while (name_hash > 0) { + ret = ocfs2_xattr_get_rec(inode, name_hash, &p_blkno, + &e_cpos, &num_clusters, el); + if (ret) { + mlog_errno(ret); + goto out; + } + + ret = ocfs2_iterate_xattr_buckets(inode, p_blkno, num_clusters, + ocfs2_list_xattr_bucket, + &xl); + if (ret) { + mlog_errno(ret); + goto out; + } + + if (e_cpos == 0) + break; + + name_hash = e_cpos - 1; + } + + ret = xl.result; +out: + return ret; +} + +static int cmp_xe(const void *a, const void *b) +{ + const struct ocfs2_xattr_entry *l = a, *r = b; + u32 l_hash = le32_to_cpu(l->xe_name_hash); + u32 r_hash = le32_to_cpu(r->xe_name_hash); + + if (l_hash > r_hash) + return 1; + if (l_hash < r_hash) + return -1; + return 0; +} + +static void swap_xe(void *a, void *b, int size) +{ + struct ocfs2_xattr_entry *l = a, *r = b, tmp; + + tmp = *l; + memcpy(l, r, sizeof(struct ocfs2_xattr_entry)); + memcpy(r, &tmp, sizeof(struct ocfs2_xattr_entry)); +} + +/* + * When the ocfs2_xattr_block is filled up, new bucket will be created + * and all the xattr entries will be moved to the new bucket. + * Note: we need to sort the entries since they are not saved in order + * in the ocfs2_xattr_block. + */ +static void ocfs2_cp_xattr_block_to_bucket(struct inode *inode, + struct buffer_head *xb_bh, + struct buffer_head *xh_bh, + struct buffer_head *data_bh) +{ + int i, blocksize = inode->i_sb->s_blocksize; + u16 offset, size, off_change; + struct ocfs2_xattr_entry *xe; + struct ocfs2_xattr_block *xb = + (struct ocfs2_xattr_block *)xb_bh->b_data; + struct ocfs2_xattr_header *xb_xh = &xb->xb_attrs.xb_header; + struct ocfs2_xattr_header *xh = + (struct ocfs2_xattr_header *)xh_bh->b_data; + u16 count = le16_to_cpu(xb_xh->xh_count); + char *target = xh_bh->b_data, *src = xb_bh->b_data; + + mlog(0, "cp xattr from block %llu to bucket %llu\n", + (unsigned long long)xb_bh->b_blocknr, + (unsigned long long)xh_bh->b_blocknr); + + memset(xh_bh->b_data, 0, blocksize); + if (data_bh) + memset(data_bh->b_data, 0, blocksize); + /* + * Since the xe_name_offset is based on ocfs2_xattr_header, + * there is a offset change corresponding to the change of + * ocfs2_xattr_header's position. + */ + off_change = offsetof(struct ocfs2_xattr_block, xb_attrs.xb_header); + xe = &xb_xh->xh_entries[count - 1]; + offset = le16_to_cpu(xe->xe_name_offset) + off_change; + size = blocksize - offset; + + /* copy all the names and values. */ + if (data_bh) + target = data_bh->b_data; + memcpy(target + offset, src + offset, size); + + /* Init new header now. */ + xh->xh_count = xb_xh->xh_count; + xh->xh_num_buckets = cpu_to_le16(1); + xh->xh_name_value_len = cpu_to_le16(size); + xh->xh_free_start = cpu_to_le16(OCFS2_XATTR_BUCKET_SIZE - size); + + /* copy all the entries. */ + target = xh_bh->b_data; + offset = offsetof(struct ocfs2_xattr_header, xh_entries); + size = count * sizeof(struct ocfs2_xattr_entry); + memcpy(target + offset, (char *)xb_xh + offset, size); + + /* Change the xe offset for all the xe because of the move. */ + off_change = OCFS2_XATTR_BUCKET_SIZE - blocksize + + offsetof(struct ocfs2_xattr_block, xb_attrs.xb_header); + for (i = 0; i < count; i++) + le16_add_cpu(&xh->xh_entries[i].xe_name_offset, off_change); + + mlog(0, "copy entry: start = %u, size = %u, offset_change = %u\n", + offset, size, off_change); + + sort(target + offset, count, sizeof(struct ocfs2_xattr_entry), + cmp_xe, swap_xe); +} + +/* + * After we move xattr from block to index btree, we have to + * update ocfs2_xattr_search to the new xe and base. + * + * When the entry is in xattr block, xattr_bh indicates the storage place. + * While if the entry is in index b-tree, "bucket" indicates the + * real place of the xattr. + */ +static int ocfs2_xattr_update_xattr_search(struct inode *inode, + struct ocfs2_xattr_search *xs, + struct buffer_head *old_bh, + struct buffer_head *new_bh) +{ + int ret = 0; + char *buf = old_bh->b_data; + struct ocfs2_xattr_block *old_xb = (struct ocfs2_xattr_block *)buf; + struct ocfs2_xattr_header *old_xh = &old_xb->xb_attrs.xb_header; + int i, blocksize = inode->i_sb->s_blocksize; + u16 blk_per_bucket = ocfs2_blocks_per_xattr_bucket(inode->i_sb); + + xs->bucket.bhs[0] = new_bh; + get_bh(new_bh); + xs->bucket.xh = (struct ocfs2_xattr_header *)xs->bucket.bhs[0]->b_data; + xs->header = xs->bucket.xh; + + xs->base = new_bh->b_data; + xs->end = xs->base + inode->i_sb->s_blocksize; + + if (!xs->not_found) { + if (OCFS2_XATTR_BUCKET_SIZE != blocksize) { + ret = ocfs2_read_blocks(inode, + xs->bucket.bhs[0]->b_blocknr + 1, + blk_per_bucket - 1, &xs->bucket.bhs[1], + OCFS2_BH_CACHED); + if (ret) { + mlog_errno(ret); + return ret; + } + + i = xs->here - old_xh->xh_entries; + xs->here = &xs->header->xh_entries[i]; + } + } + + return ret; +} + +static int ocfs2_xattr_create_index_block(struct inode *inode, + struct ocfs2_xattr_search *xs) +{ + int ret, credits = OCFS2_SUBALLOC_ALLOC; + u32 bit_off, len; + u64 blkno; + handle_t *handle; + struct ocfs2_super *osb = OCFS2_SB(inode->i_sb); + struct ocfs2_inode_info *oi = OCFS2_I(inode); + struct ocfs2_alloc_context *data_ac; + struct buffer_head *xh_bh = NULL, *data_bh = NULL; + struct buffer_head *xb_bh = xs->xattr_bh; + struct ocfs2_xattr_block *xb = + (struct ocfs2_xattr_block *)xb_bh->b_data; + struct ocfs2_xattr_tree_root *xr; + u16 xb_flags = le16_to_cpu(xb->xb_flags); + u16 bpb = ocfs2_blocks_per_xattr_bucket(inode->i_sb); + + mlog(0, "create xattr index block for %llu\n", + (unsigned long long)xb_bh->b_blocknr); + + BUG_ON(xb_flags & OCFS2_XATTR_INDEXED); + + ret = ocfs2_reserve_clusters(osb, 1, &data_ac); + if (ret) { + mlog_errno(ret); + goto out; + } + + /* + * XXX: + * We can use this lock for now, and maybe move to a dedicated mutex + * if performance becomes a problem later. + */ + down_write(&oi->ip_alloc_sem); + + /* + * 3 more credits, one for xattr block update, one for the 1st block + * of the new xattr bucket and one for the value/data. + */ + credits += 3; + handle = ocfs2_start_trans(osb, credits); + if (IS_ERR(handle)) { + ret = PTR_ERR(handle); + mlog_errno(ret); + goto out_sem; + } + + ret = ocfs2_journal_access(handle, inode, xb_bh, + OCFS2_JOURNAL_ACCESS_WRITE); + if (ret) { + mlog_errno(ret); + goto out_commit; + } + + ret = ocfs2_claim_clusters(osb, handle, data_ac, 1, &bit_off, &len); + if (ret) { + mlog_errno(ret); + goto out_commit; + } + + /* + * The bucket may spread in many blocks, and + * we will only touch the 1st block and the last block + * in the whole bucket(one for entry and one for data). + */ + blkno = ocfs2_clusters_to_blocks(inode->i_sb, bit_off); + + mlog(0, "allocate 1 cluster from %llu to xattr block\n", blkno); + + xh_bh = sb_getblk(inode->i_sb, blkno); + if (!xh_bh) { + ret = -EIO; + mlog_errno(ret); + goto out_commit; + } + + ocfs2_set_new_buffer_uptodate(inode, xh_bh); + + ret = ocfs2_journal_access(handle, inode, xh_bh, + OCFS2_JOURNAL_ACCESS_CREATE); + if (ret) { + mlog_errno(ret); + goto out_commit; + } + + if (bpb > 1) { + data_bh = sb_getblk(inode->i_sb, blkno + bpb - 1); + if (!data_bh) { + ret = -EIO; + mlog_errno(ret); + goto out_commit; + } + + ocfs2_set_new_buffer_uptodate(inode, data_bh); + + ret = ocfs2_journal_access(handle, inode, data_bh, + OCFS2_JOURNAL_ACCESS_CREATE); + if (ret) { + mlog_errno(ret); + goto out_commit; + } + } + + ocfs2_cp_xattr_block_to_bucket(inode, xb_bh, xh_bh, data_bh); + + ocfs2_journal_dirty(handle, xh_bh); + if (data_bh) + ocfs2_journal_dirty(handle, data_bh); + + ocfs2_xattr_update_xattr_search(inode, xs, xb_bh, xh_bh); + + /* Change from ocfs2_xattr_header to ocfs2_xattr_tree_root */ + memset(&xb->xb_attrs, 0, inode->i_sb->s_blocksize - + offsetof(struct ocfs2_xattr_block, xb_attrs)); + + xr = &xb->xb_attrs.xb_root; + xr->xt_clusters = cpu_to_le32(1); + xr->xt_last_eb_blk = 0; + xr->xt_list.l_tree_depth = 0; + xr->xt_list.l_count = cpu_to_le16(ocfs2_xattr_recs_per_xb(inode->i_sb)); + xr->xt_list.l_next_free_rec = cpu_to_le16(1); + + xr->xt_list.l_recs[0].e_cpos = 0; + xr->xt_list.l_recs[0].e_blkno = cpu_to_le64(blkno); + xr->xt_list.l_recs[0].e_leaf_clusters = cpu_to_le16(1); + + xb->xb_flags = cpu_to_le16(xb_flags | OCFS2_XATTR_INDEXED); + + ret = ocfs2_journal_dirty(handle, xb_bh); + if (ret) { + mlog_errno(ret); + goto out_commit; + } + +out_commit: + ocfs2_commit_trans(osb, handle); + +out_sem: + up_write(&oi->ip_alloc_sem); + +out: + if (data_ac) + ocfs2_free_alloc_context(data_ac); + + brelse(xh_bh); + brelse(data_bh); + + return ret; +} + +static int cmp_xe_offset(const void *a, const void *b) +{ + const struct ocfs2_xattr_entry *l = a, *r = b; + u32 l_name_offset = le16_to_cpu(l->xe_name_offset); + u32 r_name_offset = le16_to_cpu(r->xe_name_offset); + + if (l_name_offset < r_name_offset) + return 1; + if (l_name_offset > r_name_offset) + return -1; + return 0; +} + +/* + * defrag a xattr bucket if we find that the bucket has some + * holes beteen name/value pairs. + * We will move all the name/value pairs to the end of the bucket + * so that we can spare some space for insertion. + */ +static int ocfs2_defrag_xattr_bucket(struct inode *inode, + struct ocfs2_xattr_bucket *bucket) +{ + int ret, i; + size_t end, offset, len, value_len; + struct ocfs2_xattr_header *xh; + char *entries, *buf, *bucket_buf = NULL; + u64 blkno = bucket->bhs[0]->b_blocknr; + u16 blk_per_bucket = ocfs2_blocks_per_xattr_bucket(inode->i_sb); + u16 xh_free_start; + size_t blocksize = inode->i_sb->s_blocksize; + handle_t *handle; + struct buffer_head **bhs; + struct ocfs2_xattr_entry *xe; + + bhs = kzalloc(sizeof(struct buffer_head *) * blk_per_bucket, + GFP_NOFS); + if (!bhs) + return -ENOMEM; + + ret = ocfs2_read_blocks(inode, blkno, blk_per_bucket, bhs, + OCFS2_BH_CACHED); + if (ret) + goto out; + + /* + * In order to make the operation more efficient and generic, + * we copy all the blocks into a contiguous memory and do the + * defragment there, so if anything is error, we will not touch + * the real block. + */ + bucket_buf = kmalloc(OCFS2_XATTR_BUCKET_SIZE, GFP_NOFS); + if (!bucket_buf) { + ret = -EIO; + goto out; + } + + buf = bucket_buf; + for (i = 0; i < blk_per_bucket; i++, buf += blocksize) + memcpy(buf, bhs[i]->b_data, blocksize); + + handle = ocfs2_start_trans((OCFS2_SB(inode->i_sb)), blk_per_bucket); + if (IS_ERR(handle)) { + ret = PTR_ERR(handle); + handle = NULL; + mlog_errno(ret); + goto out; + } + + for (i = 0; i < blk_per_bucket; i++) { + ret = ocfs2_journal_access(handle, inode, bhs[i], + OCFS2_JOURNAL_ACCESS_WRITE); + if (ret < 0) { + mlog_errno(ret); + goto commit; + } + } + + xh = (struct ocfs2_xattr_header *)bucket_buf; + entries = (char *)xh->xh_entries; + xh_free_start = le16_to_cpu(xh->xh_free_start); + + mlog(0, "adjust xattr bucket in %llu, count = %u, " + "xh_free_start = %u, xh_name_value_len = %u.\n", + blkno, le16_to_cpu(xh->xh_count), xh_free_start, + le16_to_cpu(xh->xh_name_value_len)); + + /* + * sort all the entries by their offset. + * the largest will be the first, so that we can + * move them to the end one by one. + */ + sort(entries, le16_to_cpu(xh->xh_count), + sizeof(struct ocfs2_xattr_entry), + cmp_xe_offset, swap_xe); + + /* Move all name/values to the end of the bucket. */ + xe = xh->xh_entries; + end = OCFS2_XATTR_BUCKET_SIZE; + for (i = 0; i < le16_to_cpu(xh->xh_count); i++, xe++) { + offset = le16_to_cpu(xe->xe_name_offset); + if (ocfs2_xattr_is_local(xe)) + value_len = OCFS2_XATTR_SIZE( + le64_to_cpu(xe->xe_value_size)); + else + value_len = OCFS2_XATTR_ROOT_SIZE; + len = OCFS2_XATTR_SIZE(xe->xe_name_len) + value_len; + + /* + * We must make sure that the name/value pair + * exist in the same block. So adjust end to + * the previous block end if needed. + */ + if (((end - len) / blocksize != + (end - 1) / blocksize)) + end = end - end % blocksize; + + if (end > offset + len) { + memmove(bucket_buf + end - len, + bucket_buf + offset, len); + xe->xe_name_offset = cpu_to_le16(end - len); + } + + mlog_bug_on_msg(end < offset + len, "Defrag check failed for " + "bucket %llu\n", (unsigned long long)blkno); + + end -= len; + } + + mlog_bug_on_msg(xh_free_start > end, "Defrag check failed for " + "bucket %llu\n", (unsigned long long)blkno); + + if (xh_free_start == end) + goto commit; + + memset(bucket_buf + xh_free_start, 0, end - xh_free_start); + xh->xh_free_start = cpu_to_le16(end); + + /* sort the entries by their name_hash. */ + sort(entries, le16_to_cpu(xh->xh_count), + sizeof(struct ocfs2_xattr_entry), + cmp_xe, swap_xe); + + buf = bucket_buf; + for (i = 0; i < blk_per_bucket; i++, buf += blocksize) { + memcpy(bhs[i]->b_data, buf, blocksize); + ocfs2_journal_dirty(handle, bhs[i]); + } + +commit: + ocfs2_commit_trans(OCFS2_SB(inode->i_sb), handle); +out: + + if (bhs) { + for (i = 0; i < blk_per_bucket; i++) + brelse(bhs[i]); + } + kfree(bhs); + + kfree(bucket_buf); + return ret; +} + +/* + * Move half nums of the xattr bucket in the previous cluster to this new + * cluster. We only touch the last cluster of the previous extend record. + * + * first_bh is the first buffer_head of a series of bucket in the same + * extent rec and header_bh is the header of one bucket in this cluster. + * They will be updated if we move the data header_bh contains to the new + * cluster. first_hash will be set as the 1st xe's name_hash of the new cluster. + */ +static int ocfs2_mv_xattr_bucket_cross_cluster(struct inode *inode, + handle_t *handle, + struct buffer_head **first_bh, + struct buffer_head **header_bh, + u64 new_blkno, + u64 prev_blkno, + u32 num_clusters, + u32 *first_hash) +{ + int i, ret, credits; + struct ocfs2_super *osb = OCFS2_SB(inode->i_sb); + int bpc = ocfs2_clusters_to_blocks(inode->i_sb, 1); + int num_buckets = ocfs2_xattr_buckets_per_cluster(osb); + int blocksize = inode->i_sb->s_blocksize; + struct buffer_head *old_bh, *new_bh, *prev_bh, *new_first_bh = NULL; + struct ocfs2_xattr_header *new_xh; + struct ocfs2_xattr_header *xh = + (struct ocfs2_xattr_header *)((*first_bh)->b_data); + + BUG_ON(le16_to_cpu(xh->xh_num_buckets) < num_buckets); + BUG_ON(OCFS2_XATTR_BUCKET_SIZE == osb->s_clustersize); + + prev_bh = *first_bh; + get_bh(prev_bh); + xh = (struct ocfs2_xattr_header *)prev_bh->b_data; + + prev_blkno += (num_clusters - 1) * bpc + bpc / 2; + + mlog(0, "move half of xattrs in cluster %llu to %llu\n", + prev_blkno, new_blkno); + + /* + * We need to update the 1st half of the new cluster and + * 1 more for the update of the 1st bucket of the previous + * extent record. + */ + credits = bpc / 2 + 1; + ret = ocfs2_extend_trans(handle, credits); + if (ret) { + mlog_errno(ret); + goto out; + } + + ret = ocfs2_journal_access(handle, inode, prev_bh, + OCFS2_JOURNAL_ACCESS_WRITE); + if (ret) { + mlog_errno(ret); + goto out; + } + + for (i = 0; i < bpc / 2; i++, prev_blkno++, new_blkno++) { + old_bh = new_bh = NULL; + new_bh = sb_getblk(inode->i_sb, new_blkno); + if (!new_bh) { + ret = -EIO; + mlog_errno(ret); + goto out; + } + + ocfs2_set_new_buffer_uptodate(inode, new_bh); + + ret = ocfs2_journal_access(handle, inode, new_bh, + OCFS2_JOURNAL_ACCESS_CREATE); + if (ret < 0) { + mlog_errno(ret); + brelse(new_bh); + goto out; + } + + ret = ocfs2_read_block(inode, prev_blkno, &old_bh); + if (ret < 0) { + mlog_errno(ret); + brelse(new_bh); + goto out; + } + + memcpy(new_bh->b_data, old_bh->b_data, blocksize); + + if (i == 0) { + new_xh = (struct ocfs2_xattr_header *)new_bh->b_data; + new_xh->xh_num_buckets = cpu_to_le16(num_buckets / 2); + + if (first_hash) + *first_hash = le32_to_cpu( + new_xh->xh_entries[0].xe_name_hash); + new_first_bh = new_bh; + get_bh(new_first_bh); + } + + ocfs2_journal_dirty(handle, new_bh); + + if (*header_bh == old_bh) { + brelse(*header_bh); + *header_bh = new_bh; + get_bh(*header_bh); + + brelse(*first_bh); + *first_bh = new_first_bh; + get_bh(*first_bh); + } + brelse(new_bh); + brelse(old_bh); + } + + le16_add_cpu(&xh->xh_num_buckets, -(num_buckets / 2)); + + ocfs2_journal_dirty(handle, prev_bh); +out: + brelse(prev_bh); + brelse(new_first_bh); + return ret; +} + +static int ocfs2_read_xattr_bucket(struct inode *inode, + u64 blkno, + struct buffer_head **bhs, + int new) +{ + int ret = 0; + u16 i, blk_per_bucket = ocfs2_blocks_per_xattr_bucket(inode->i_sb); + + if (!new) + return ocfs2_read_blocks(inode, blkno, + blk_per_bucket, bhs, + OCFS2_BH_CACHED); + + for (i = 0; i < blk_per_bucket; i++) { + bhs[i] = sb_getblk(inode->i_sb, blkno + i); + if (bhs[i] == NULL) { + ret = -EIO; + mlog_errno(ret); + break; + } + ocfs2_set_new_buffer_uptodate(inode, bhs[i]); + } + + return ret; +} + +/* + * Move half num of the xattrs in old bucket(blk) to new bucket(new_blk). + * first_hash will record the 1st hash of the new bucket. + */ +static int ocfs2_half_xattr_bucket(struct inode *inode, + handle_t *handle, + u64 blk, + u64 new_blk, + u32 *first_hash, + int new_bucket_head) +{ + int ret, i; + u16 count, start, len, name_value_len, xe_len, name_offset; + u16 blk_per_bucket = ocfs2_blocks_per_xattr_bucket(inode->i_sb); + struct buffer_head **s_bhs, **t_bhs = NULL; + struct ocfs2_xattr_header *xh; + struct ocfs2_xattr_entry *xe; + int blocksize = inode->i_sb->s_blocksize; + + mlog(0, "move half of xattrs from bucket %llu to %llu\n", + blk, new_blk); + + s_bhs = kcalloc(blk_per_bucket, sizeof(struct buffer_head *), GFP_NOFS); + if (!s_bhs) + return -ENOMEM; + + ret = ocfs2_read_xattr_bucket(inode, blk, s_bhs, 0); + if (ret) { + mlog_errno(ret); + goto out; + } + + ret = ocfs2_journal_access(handle, inode, s_bhs[0], + OCFS2_JOURNAL_ACCESS_WRITE); + if (ret) { + mlog_errno(ret); + goto out; + } + + t_bhs = kcalloc(blk_per_bucket, sizeof(struct buffer_head *), GFP_NOFS); + if (!t_bhs) { + ret = -ENOMEM; + goto out; + } + + ret = ocfs2_read_xattr_bucket(inode, new_blk, t_bhs, new_bucket_head); + if (ret) { + mlog_errno(ret); + goto out; + } + + for (i = 0; i < blk_per_bucket; i++) { + ret = ocfs2_journal_access(handle, inode, t_bhs[i], + OCFS2_JOURNAL_ACCESS_CREATE); + if (ret) { + mlog_errno(ret); + goto out; + } + } + + /* copy the whole bucket to the new first. */ + for (i = 0; i < blk_per_bucket; i++) + memcpy(t_bhs[i]->b_data, s_bhs[i]->b_data, blocksize); + + /* update the new bucket. */ + xh = (struct ocfs2_xattr_header *)t_bhs[0]->b_data; + count = le16_to_cpu(xh->xh_count); + start = count / 2; + + /* + * Calculate the total name/value len and xh_free_start for + * the old bucket first. + */ + name_offset = OCFS2_XATTR_BUCKET_SIZE; + name_value_len = 0; + for (i = 0; i < start; i++) { + xe = &xh->xh_entries[i]; + xe_len = OCFS2_XATTR_SIZE(xe->xe_name_len); + if (ocfs2_xattr_is_local(xe)) + xe_len += + OCFS2_XATTR_SIZE(le64_to_cpu(xe->xe_value_size)); + else + xe_len += OCFS2_XATTR_ROOT_SIZE; + name_value_len += xe_len; + if (le16_to_cpu(xe->xe_name_offset) < name_offset) + name_offset = le16_to_cpu(xe->xe_name_offset); + } + + /* + * Now begin the modification to the new bucket. + * + * In the new bucket, We just move the xattr entry to the beginning + * and don't touch the name/value. So there will be some holes in the + * bucket, and they will be removed when ocfs2_defrag_xattr_bucket is + * called. + */ + xe = &xh->xh_entries[start]; + len = sizeof(struct ocfs2_xattr_entry) * (count - start); + mlog(0, "mv xattr entry len %d from %d to %d\n", len, + (int)((char *)xe - (char *)xh), + (int)((char *)xh->xh_entries - (char *)xh)); + memmove((char *)xh->xh_entries, (char *)xe, len); + xe = &xh->xh_entries[count - start]; + len = sizeof(struct ocfs2_xattr_entry) * start; + memset((char *)xe, 0, len); + + le16_add_cpu(&xh->xh_count, -start); + le16_add_cpu(&xh->xh_name_value_len, -name_value_len); + + /* Calculate xh_free_start for the new bucket. */ + xh->xh_free_start = cpu_to_le16(OCFS2_XATTR_BUCKET_SIZE); + for (i = 0; i < le16_to_cpu(xh->xh_count); i++) { + xe = &xh->xh_entries[i]; + xe_len = OCFS2_XATTR_SIZE(xe->xe_name_len); + if (ocfs2_xattr_is_local(xe)) + xe_len += + OCFS2_XATTR_SIZE(le64_to_cpu(xe->xe_value_size)); + else + xe_len += OCFS2_XATTR_ROOT_SIZE; + if (le16_to_cpu(xe->xe_name_offset) < + le16_to_cpu(xh->xh_free_start)) + xh->xh_free_start = xe->xe_name_offset; + } + + /* set xh->xh_num_buckets for the new xh. */ + if (new_bucket_head) + xh->xh_num_buckets = cpu_to_le16(1); + else + xh->xh_num_buckets = 0; + + for (i = 0; i < blk_per_bucket; i++) { + ocfs2_journal_dirty(handle, t_bhs[i]); + if (ret) + mlog_errno(ret); + } + + /* store the first_hash of the new bucket. */ + if (first_hash) + *first_hash = le32_to_cpu(xh->xh_entries[0].xe_name_hash); + + /* + * Now only update the 1st block of the old bucket. + * Please note that the entry has been sorted already above. + */ + xh = (struct ocfs2_xattr_header *)s_bhs[0]->b_data; + memset(&xh->xh_entries[start], 0, + sizeof(struct ocfs2_xattr_entry) * (count - start)); + xh->xh_count = cpu_to_le16(start); + xh->xh_free_start = cpu_to_le16(name_offset); + xh->xh_name_value_len = cpu_to_le16(name_value_len); + + ocfs2_journal_dirty(handle, s_bhs[0]); + if (ret) + mlog_errno(ret); + +out: + if (s_bhs) { + for (i = 0; i < blk_per_bucket; i++) + brelse(s_bhs[i]); + } + kfree(s_bhs); + + if (t_bhs) { + for (i = 0; i < blk_per_bucket; i++) + brelse(t_bhs[i]); + } + kfree(t_bhs); + + return ret; +} + +/* + * Copy xattr from one bucket to another bucket. + * + * The caller must make sure that the journal transaction + * has enough space for journaling. + */ +static int ocfs2_cp_xattr_bucket(struct inode *inode, + handle_t *handle, + u64 s_blkno, + u64 t_blkno, + int t_is_new) +{ + int ret, i; + int blk_per_bucket = ocfs2_blocks_per_xattr_bucket(inode->i_sb); + int blocksize = inode->i_sb->s_blocksize; + struct buffer_head **s_bhs, **t_bhs = NULL; + + BUG_ON(s_blkno == t_blkno); + + mlog(0, "cp bucket %llu to %llu, target is %d\n", + s_blkno, t_blkno, t_is_new); + + s_bhs = kzalloc(sizeof(struct buffer_head *) * blk_per_bucket, + GFP_NOFS); + if (!s_bhs) + return -ENOMEM; + + ret = ocfs2_read_xattr_bucket(inode, s_blkno, s_bhs, 0); + if (ret) + goto out; + + t_bhs = kzalloc(sizeof(struct buffer_head *) * blk_per_bucket, + GFP_NOFS); + if (!t_bhs) { + ret = -ENOMEM; + goto out; + } + + ret = ocfs2_read_xattr_bucket(inode, t_blkno, t_bhs, t_is_new); + if (ret) + goto out; + + for (i = 0; i < blk_per_bucket; i++) { + ret = ocfs2_journal_access(handle, inode, t_bhs[i], + OCFS2_JOURNAL_ACCESS_WRITE); + if (ret) + goto out; + } + + for (i = 0; i < blk_per_bucket; i++) { + memcpy(t_bhs[i]->b_data, s_bhs[i]->b_data, blocksize); + ocfs2_journal_dirty(handle, t_bhs[i]); + } + +out: + if (s_bhs) { + for (i = 0; i < blk_per_bucket; i++) + brelse(s_bhs[i]); + } + kfree(s_bhs); + + if (t_bhs) { + for (i = 0; i < blk_per_bucket; i++) + brelse(t_bhs[i]); + } + kfree(t_bhs); + + return ret; +} + +/* + * Copy one xattr cluster from src_blk to to_blk. + * The to_blk will become the first bucket header of the cluster, so its + * xh_num_buckets will be initialized as the bucket num in the cluster. + */ +static int ocfs2_cp_xattr_cluster(struct inode *inode, + handle_t *handle, + struct buffer_head *first_bh, + u64 src_blk, + u64 to_blk, + u32 *first_hash) +{ + int i, ret, credits; + struct ocfs2_super *osb = OCFS2_SB(inode->i_sb); + int bpc = ocfs2_clusters_to_blocks(inode->i_sb, 1); + int num_buckets = ocfs2_xattr_buckets_per_cluster(osb); + struct buffer_head *bh = NULL; + struct ocfs2_xattr_header *xh; + u64 to_blk_start = to_blk; + + mlog(0, "cp xattrs from cluster %llu to %llu\n", src_blk, to_blk); + + /* + * We need to update the new cluster and 1 more for the update of + * the 1st bucket of the previous extent rec. + */ + credits = bpc + 1; + ret = ocfs2_extend_trans(handle, credits); + if (ret) { + mlog_errno(ret); + goto out; + } + + ret = ocfs2_journal_access(handle, inode, first_bh, + OCFS2_JOURNAL_ACCESS_WRITE); + if (ret) { + mlog_errno(ret); + goto out; + } + + for (i = 0; i < num_buckets; i++) { + ret = ocfs2_cp_xattr_bucket(inode, handle, + src_blk, to_blk, 1); + if (ret) { + mlog_errno(ret); + goto out; + } + + src_blk += ocfs2_blocks_per_xattr_bucket(inode->i_sb); + to_blk += ocfs2_blocks_per_xattr_bucket(inode->i_sb); + } + + /* update the old bucket header. */ + xh = (struct ocfs2_xattr_header *)first_bh->b_data; + le16_add_cpu(&xh->xh_num_buckets, -num_buckets); + + ocfs2_journal_dirty(handle, first_bh); + + /* update the new bucket header. */ + ret = ocfs2_read_block(inode, to_blk_start, &bh); + if (ret < 0) { + mlog_errno(ret); + goto out; + } + + ret = ocfs2_journal_access(handle, inode, bh, + OCFS2_JOURNAL_ACCESS_WRITE); + if (ret) { + mlog_errno(ret); + goto out; + } + + xh = (struct ocfs2_xattr_header *)bh->b_data; + xh->xh_num_buckets = cpu_to_le16(num_buckets); + + ocfs2_journal_dirty(handle, bh); + + if (first_hash) + *first_hash = le32_to_cpu(xh->xh_entries[0].xe_name_hash); +out: + brelse(bh); + return ret; +} + +/* + * Move half of the xattrs in this cluster to the new cluster. + * This function should only be called when bucket size == cluster size. + * Otherwise ocfs2_mv_xattr_bucket_cross_cluster should be used instead. + */ +static int ocfs2_half_xattr_cluster(struct inode *inode, + handle_t *handle, + u64 prev_blk, + u64 new_blk, + u32 *first_hash) +{ + u16 blk_per_bucket = ocfs2_blocks_per_xattr_bucket(inode->i_sb); + int ret, credits = 2 * blk_per_bucket; + + BUG_ON(OCFS2_XATTR_BUCKET_SIZE < OCFS2_SB(inode->i_sb)->s_clustersize); + + ret = ocfs2_extend_trans(handle, credits); + if (ret) { + mlog_errno(ret); + return ret; + } + + /* Move half of the xattr in start_blk to the next bucket. */ + return ocfs2_half_xattr_bucket(inode, handle, prev_blk, + new_blk, first_hash, 1); +} + +/* + * Move some xattrs from the old cluster to the new one since they are not + * contiguous in ocfs2 xattr tree. + * + * new_blk starts a new separate cluster, and we will move some xattrs from + * prev_blk to it. v_start will be set as the first name hash value in this + * new cluster so that it can be used as e_cpos during tree insertion and + * don't collide with our original b-tree operations. first_bh and header_bh + * will also be updated since they will be used in ocfs2_extend_xattr_bucket + * to extend the insert bucket. + * + * The problem is how much xattr should we move to the new one and when should + * we update first_bh and header_bh? + * 1. If cluster size > bucket size, that means the previous cluster has more + * than 1 bucket, so just move half nums of bucket into the new cluster and + * update the first_bh and header_bh if the insert bucket has been moved + * to the new cluster. + * 2. If cluster_size == bucket_size: + * a) If the previous extent rec has more than one cluster and the insert + * place isn't in the last cluster, copy the entire last cluster to the + * new one. This time, we don't need to upate the first_bh and header_bh + * since they will not be moved into the new cluster. + * b) Otherwise, move the bottom half of the xattrs in the last cluster into + * the new one. And we set the extend flag to zero if the insert place is + * moved into the new allocated cluster since no extend is needed. + */ +static int ocfs2_adjust_xattr_cross_cluster(struct inode *inode, + handle_t *handle, + struct buffer_head **first_bh, + struct buffer_head **header_bh, + u64 new_blk, + u64 prev_blk, + u32 prev_clusters, + u32 *v_start, + int *extend) +{ + int ret = 0; + int bpc = ocfs2_clusters_to_blocks(inode->i_sb, 1); + + mlog(0, "adjust xattrs from cluster %llu len %u to %llu\n", + prev_blk, prev_clusters, new_blk); + + if (ocfs2_xattr_buckets_per_cluster(OCFS2_SB(inode->i_sb)) > 1) + ret = ocfs2_mv_xattr_bucket_cross_cluster(inode, + handle, + first_bh, + header_bh, + new_blk, + prev_blk, + prev_clusters, + v_start); + else { + u64 last_blk = prev_blk + bpc * (prev_clusters - 1); + + if (prev_clusters > 1 && (*header_bh)->b_blocknr != last_blk) + ret = ocfs2_cp_xattr_cluster(inode, handle, *first_bh, + last_blk, new_blk, + v_start); + else { + ret = ocfs2_half_xattr_cluster(inode, handle, + last_blk, new_blk, + v_start); + + if ((*header_bh)->b_blocknr == last_blk && extend) + *extend = 0; + } + } + + return ret; +} + +/* + * Add a new cluster for xattr storage. + * + * If the new cluster is contiguous with the previous one, it will be + * appended to the same extent record, and num_clusters will be updated. + * If not, we will insert a new extent for it and move some xattrs in + * the last cluster into the new allocated one. + * We also need to limit the maximum size of a btree leaf, otherwise we'll + * lose the benefits of hashing because we'll have to search large leaves. + * So now the maximum size is OCFS2_MAX_XATTR_TREE_LEAF_SIZE(or clustersize, + * if it's bigger). + * + * first_bh is the first block of the previous extent rec and header_bh + * indicates the bucket we will insert the new xattrs. They will be updated + * when the header_bh is moved into the new cluster. + */ +static int ocfs2_add_new_xattr_cluster(struct inode *inode, + struct buffer_head *root_bh, + struct buffer_head **first_bh, + struct buffer_head **header_bh, + u32 *num_clusters, + u32 prev_cpos, + u64 prev_blkno, + int *extend) +{ + int ret, credits; + u16 bpc = ocfs2_clusters_to_blocks(inode->i_sb, 1); + u32 prev_clusters = *num_clusters; + u32 clusters_to_add = 1, bit_off, num_bits, v_start = 0; + u64 block; + handle_t *handle = NULL; + struct ocfs2_alloc_context *data_ac = NULL; + struct ocfs2_alloc_context *meta_ac = NULL; + struct ocfs2_super *osb = OCFS2_SB(inode->i_sb); + struct ocfs2_extent_tree et; + + mlog(0, "Add new xattr cluster for %llu, previous xattr hash = %u, " + "previous xattr blkno = %llu\n", + (unsigned long long)OCFS2_I(inode)->ip_blkno, + prev_cpos, prev_blkno); + + ocfs2_init_xattr_tree_extent_tree(&et, inode, root_bh); + + ret = ocfs2_lock_allocators(inode, &et, clusters_to_add, 0, + &data_ac, &meta_ac); + if (ret) { + mlog_errno(ret); + goto leave; + } + + credits = ocfs2_calc_extend_credits(osb->sb, et.et_root_el, + clusters_to_add); + handle = ocfs2_start_trans(osb, credits); + if (IS_ERR(handle)) { + ret = PTR_ERR(handle); + handle = NULL; + mlog_errno(ret); + goto leave; + } + + ret = ocfs2_journal_access(handle, inode, root_bh, + OCFS2_JOURNAL_ACCESS_WRITE); + if (ret < 0) { + mlog_errno(ret); + goto leave; + } + + ret = __ocfs2_claim_clusters(osb, handle, data_ac, 1, + clusters_to_add, &bit_off, &num_bits); + if (ret < 0) { + if (ret != -ENOSPC) + mlog_errno(ret); + goto leave; + } + + BUG_ON(num_bits > clusters_to_add); + + block = ocfs2_clusters_to_blocks(osb->sb, bit_off); + mlog(0, "Allocating %u clusters at block %u for xattr in inode %llu\n", + num_bits, bit_off, (unsigned long long)OCFS2_I(inode)->ip_blkno); + + if (prev_blkno + prev_clusters * bpc == block && + (prev_clusters + num_bits) << osb->s_clustersize_bits <= + OCFS2_MAX_XATTR_TREE_LEAF_SIZE) { + /* + * If this cluster is contiguous with the old one and + * adding this new cluster, we don't surpass the limit of + * OCFS2_MAX_XATTR_TREE_LEAF_SIZE, cool. We will let it be + * initialized and used like other buckets in the previous + * cluster. + * So add it as a contiguous one. The caller will handle + * its init process. + */ + v_start = prev_cpos + prev_clusters; + *num_clusters = prev_clusters + num_bits; + mlog(0, "Add contiguous %u clusters to previous extent rec.\n", + num_bits); + } else { + ret = ocfs2_adjust_xattr_cross_cluster(inode, + handle, + first_bh, + header_bh, + block, + prev_blkno, + prev_clusters, + &v_start, + extend); + if (ret) { + mlog_errno(ret); + goto leave; + } + } + + if (handle->h_buffer_credits < credits) { + /* + * The journal has been restarted before, and don't + * have enough space for the insertion, so extend it + * here. + */ + ret = ocfs2_extend_trans(handle, credits); + if (ret) { + mlog_errno(ret); + goto leave; + } + } + mlog(0, "Insert %u clusters at block %llu for xattr at %u\n", + num_bits, block, v_start); + ret = ocfs2_insert_extent(osb, handle, inode, &et, v_start, block, + num_bits, 0, meta_ac); + if (ret < 0) { + mlog_errno(ret); + goto leave; + } + + ret = ocfs2_journal_dirty(handle, root_bh); + if (ret < 0) { + mlog_errno(ret); + goto leave; + } + +leave: + if (handle) + ocfs2_commit_trans(osb, handle); + if (data_ac) + ocfs2_free_alloc_context(data_ac); + if (meta_ac) + ocfs2_free_alloc_context(meta_ac); + + return ret; +} + +/* + * Extend a new xattr bucket and move xattrs to the end one by one until + * We meet with start_bh. Only move half of the xattrs to the bucket after it. + */ +static int ocfs2_extend_xattr_bucket(struct inode *inode, + struct buffer_head *first_bh, + struct buffer_head *start_bh, + u32 num_clusters) +{ + int ret, credits; + struct ocfs2_super *osb = OCFS2_SB(inode->i_sb); + u16 blk_per_bucket = ocfs2_blocks_per_xattr_bucket(inode->i_sb); + u64 start_blk = start_bh->b_blocknr, end_blk; + u32 num_buckets = num_clusters * ocfs2_xattr_buckets_per_cluster(osb); + handle_t *handle; + struct ocfs2_xattr_header *first_xh = + (struct ocfs2_xattr_header *)first_bh->b_data; + u16 bucket = le16_to_cpu(first_xh->xh_num_buckets); + + mlog(0, "extend xattr bucket in %llu, xattr extend rec starting " + "from %llu, len = %u\n", start_blk, + (unsigned long long)first_bh->b_blocknr, num_clusters); + + BUG_ON(bucket >= num_buckets); + + end_blk = first_bh->b_blocknr + (bucket - 1) * blk_per_bucket; + + /* + * We will touch all the buckets after the start_bh(include it). + * Add one more bucket and modify the first_bh. + */ + credits = end_blk - start_blk + 2 * blk_per_bucket + 1; + handle = ocfs2_start_trans(osb, credits); + if (IS_ERR(handle)) { + ret = PTR_ERR(handle); + handle = NULL; + mlog_errno(ret); + goto out; + } + + ret = ocfs2_journal_access(handle, inode, first_bh, + OCFS2_JOURNAL_ACCESS_WRITE); + if (ret) { + mlog_errno(ret); + goto commit; + } + + while (end_blk != start_blk) { + ret = ocfs2_cp_xattr_bucket(inode, handle, end_blk, + end_blk + blk_per_bucket, 0); + if (ret) + goto commit; + end_blk -= blk_per_bucket; + } + + /* Move half of the xattr in start_blk to the next bucket. */ + ret = ocfs2_half_xattr_bucket(inode, handle, start_blk, + start_blk + blk_per_bucket, NULL, 0); + + le16_add_cpu(&first_xh->xh_num_buckets, 1); + ocfs2_journal_dirty(handle, first_bh); + +commit: + ocfs2_commit_trans(osb, handle); +out: + return ret; +} + +/* + * Add new xattr bucket in an extent record and adjust the buckets accordingly. + * xb_bh is the ocfs2_xattr_block. + * We will move all the buckets starting from header_bh to the next place. As + * for this one, half num of its xattrs will be moved to the next one. + * + * We will allocate a new cluster if current cluster is full and adjust + * header_bh and first_bh if the insert place is moved to the new cluster. + */ +static int ocfs2_add_new_xattr_bucket(struct inode *inode, + struct buffer_head *xb_bh, + struct buffer_head *header_bh) +{ + struct ocfs2_xattr_header *first_xh = NULL; + struct buffer_head *first_bh = NULL; + struct ocfs2_xattr_block *xb = + (struct ocfs2_xattr_block *)xb_bh->b_data; + struct ocfs2_xattr_tree_root *xb_root = &xb->xb_attrs.xb_root; + struct ocfs2_extent_list *el = &xb_root->xt_list; + struct ocfs2_xattr_header *xh = + (struct ocfs2_xattr_header *)header_bh->b_data; + u32 name_hash = le32_to_cpu(xh->xh_entries[0].xe_name_hash); + struct super_block *sb = inode->i_sb; + struct ocfs2_super *osb = OCFS2_SB(sb); + int ret, num_buckets, extend = 1; + u64 p_blkno; + u32 e_cpos, num_clusters; + + mlog(0, "Add new xattr bucket starting form %llu\n", + (unsigned long long)header_bh->b_blocknr); + + /* + * Add refrence for header_bh here because it may be + * changed in ocfs2_add_new_xattr_cluster and we need + * to free it in the end. + */ + get_bh(header_bh); + + ret = ocfs2_xattr_get_rec(inode, name_hash, &p_blkno, &e_cpos, + &num_clusters, el); + if (ret) { + mlog_errno(ret); + goto out; + } + + ret = ocfs2_read_block(inode, p_blkno, &first_bh); + if (ret) { + mlog_errno(ret); + goto out; + } + + num_buckets = ocfs2_xattr_buckets_per_cluster(osb) * num_clusters; + first_xh = (struct ocfs2_xattr_header *)first_bh->b_data; + + if (num_buckets == le16_to_cpu(first_xh->xh_num_buckets)) { + ret = ocfs2_add_new_xattr_cluster(inode, + xb_bh, + &first_bh, + &header_bh, + &num_clusters, + e_cpos, + p_blkno, + &extend); + if (ret) { + mlog_errno(ret); + goto out; + } + } + + if (extend) + ret = ocfs2_extend_xattr_bucket(inode, + first_bh, + header_bh, + num_clusters); + if (ret) + mlog_errno(ret); +out: + brelse(first_bh); + brelse(header_bh); + return ret; +} + +static inline char *ocfs2_xattr_bucket_get_val(struct inode *inode, + struct ocfs2_xattr_bucket *bucket, + int offs) +{ + int block_off = offs >> inode->i_sb->s_blocksize_bits; + + offs = offs % inode->i_sb->s_blocksize; + return bucket->bhs[block_off]->b_data + offs; +} + +/* + * Handle the normal xattr set, including replace, delete and new. + * + * Note: "local" indicates the real data's locality. So we can't + * just its bucket locality by its length. + */ +static void ocfs2_xattr_set_entry_normal(struct inode *inode, + struct ocfs2_xattr_info *xi, + struct ocfs2_xattr_search *xs, + u32 name_hash, + int local) +{ + struct ocfs2_xattr_entry *last, *xe; + int name_len = strlen(xi->name); + struct ocfs2_xattr_header *xh = xs->header; + u16 count = le16_to_cpu(xh->xh_count), start; + size_t blocksize = inode->i_sb->s_blocksize; + char *val; + size_t offs, size, new_size; + + last = &xh->xh_entries[count]; + if (!xs->not_found) { + xe = xs->here; + offs = le16_to_cpu(xe->xe_name_offset); + if (ocfs2_xattr_is_local(xe)) + size = OCFS2_XATTR_SIZE(name_len) + + OCFS2_XATTR_SIZE(le64_to_cpu(xe->xe_value_size)); + else + size = OCFS2_XATTR_SIZE(name_len) + + OCFS2_XATTR_SIZE(OCFS2_XATTR_ROOT_SIZE); + + /* + * If the new value will be stored outside, xi->value has been + * initalized as an empty ocfs2_xattr_value_root, and the same + * goes with xi->value_len, so we can set new_size safely here. + * See ocfs2_xattr_set_in_bucket. + */ + new_size = OCFS2_XATTR_SIZE(name_len) + + OCFS2_XATTR_SIZE(xi->value_len); + + le16_add_cpu(&xh->xh_name_value_len, -size); + if (xi->value) { + if (new_size > size) + goto set_new_name_value; + + /* Now replace the old value with new one. */ + if (local) + xe->xe_value_size = cpu_to_le64(xi->value_len); + else + xe->xe_value_size = 0; + + val = ocfs2_xattr_bucket_get_val(inode, + &xs->bucket, offs); + memset(val + OCFS2_XATTR_SIZE(name_len), 0, + size - OCFS2_XATTR_SIZE(name_len)); + if (OCFS2_XATTR_SIZE(xi->value_len) > 0) + memcpy(val + OCFS2_XATTR_SIZE(name_len), + xi->value, xi->value_len); + + le16_add_cpu(&xh->xh_name_value_len, new_size); + ocfs2_xattr_set_local(xe, local); + return; + } else { + /* + * Remove the old entry if there is more than one. + * We don't remove the last entry so that we can + * use it to indicate the hash value of the empty + * bucket. + */ + last -= 1; + le16_add_cpu(&xh->xh_count, -1); + if (xh->xh_count) { + memmove(xe, xe + 1, + (void *)last - (void *)xe); + memset(last, 0, + sizeof(struct ocfs2_xattr_entry)); + } else + xh->xh_free_start = + cpu_to_le16(OCFS2_XATTR_BUCKET_SIZE); + + return; + } + } else { + /* find a new entry for insert. */ + int low = 0, high = count - 1, tmp; + struct ocfs2_xattr_entry *tmp_xe; + + while (low <= high && count) { + tmp = (low + high) / 2; + tmp_xe = &xh->xh_entries[tmp]; + + if (name_hash > le32_to_cpu(tmp_xe->xe_name_hash)) + low = tmp + 1; + else if (name_hash < + le32_to_cpu(tmp_xe->xe_name_hash)) + high = tmp - 1; + else { + low = tmp; + break; + } + } + + xe = &xh->xh_entries[low]; + if (low != count) + memmove(xe + 1, xe, (void *)last - (void *)xe); + + le16_add_cpu(&xh->xh_count, 1); + memset(xe, 0, sizeof(struct ocfs2_xattr_entry)); + xe->xe_name_hash = cpu_to_le32(name_hash); + xe->xe_name_len = name_len; + ocfs2_xattr_set_type(xe, xi->name_index); + } + +set_new_name_value: + /* Insert the new name+value. */ + size = OCFS2_XATTR_SIZE(name_len) + OCFS2_XATTR_SIZE(xi->value_len); + + /* + * We must make sure that the name/value pair + * exists in the same block. + */ + offs = le16_to_cpu(xh->xh_free_start); + start = offs - size; + + if (start >> inode->i_sb->s_blocksize_bits != + (offs - 1) >> inode->i_sb->s_blocksize_bits) { + offs = offs - offs % blocksize; + xh->xh_free_start = cpu_to_le16(offs); + } + + val = ocfs2_xattr_bucket_get_val(inode, + &xs->bucket, offs - size); + xe->xe_name_offset = cpu_to_le16(offs - size); + + memset(val, 0, size); + memcpy(val, xi->name, name_len); + memcpy(val + OCFS2_XATTR_SIZE(name_len), xi->value, xi->value_len); + + xe->xe_value_size = cpu_to_le64(xi->value_len); + ocfs2_xattr_set_local(xe, local); + xs->here = xe; + le16_add_cpu(&xh->xh_free_start, -size); + le16_add_cpu(&xh->xh_name_value_len, size); + + return; +} + +static int ocfs2_xattr_bucket_handle_journal(struct inode *inode, + handle_t *handle, + struct ocfs2_xattr_search *xs, + struct buffer_head **bhs, + u16 bh_num) +{ + int ret = 0, off, block_off; + struct ocfs2_xattr_entry *xe = xs->here; + + /* + * First calculate all the blocks we should journal_access + * and journal_dirty. The first block should always be touched. + */ + ret = ocfs2_journal_dirty(handle, bhs[0]); + if (ret) + mlog_errno(ret); + + /* calc the data. */ + off = le16_to_cpu(xe->xe_name_offset); + block_off = off >> inode->i_sb->s_blocksize_bits; + ret = ocfs2_journal_dirty(handle, bhs[block_off]); + if (ret) + mlog_errno(ret); + + return ret; +} + +/* + * Set the xattr entry in the specified bucket. + * The bucket is indicated by xs->bucket and it should have the enough + * space for the xattr insertion. + */ +static int ocfs2_xattr_set_entry_in_bucket(struct inode *inode, + struct ocfs2_xattr_info *xi, + struct ocfs2_xattr_search *xs, + u32 name_hash, + int local) +{ + int i, ret; + handle_t *handle = NULL; + u16 blk_per_bucket = ocfs2_blocks_per_xattr_bucket(inode->i_sb); + struct ocfs2_super *osb = OCFS2_SB(inode->i_sb); + + mlog(0, "Set xattr entry len = %lu index = %d in bucket %llu\n", + (unsigned long)xi->value_len, xi->name_index, + (unsigned long long)xs->bucket.bhs[0]->b_blocknr); + + if (!xs->bucket.bhs[1]) { + ret = ocfs2_read_blocks(inode, + xs->bucket.bhs[0]->b_blocknr + 1, + blk_per_bucket - 1, &xs->bucket.bhs[1], + OCFS2_BH_CACHED); + if (ret) { + mlog_errno(ret); + goto out; + } + } + + handle = ocfs2_start_trans(osb, blk_per_bucket); + if (IS_ERR(handle)) { + ret = PTR_ERR(handle); + handle = NULL; + mlog_errno(ret); + goto out; + } + + for (i = 0; i < blk_per_bucket; i++) { + ret = ocfs2_journal_access(handle, inode, xs->bucket.bhs[i], + OCFS2_JOURNAL_ACCESS_WRITE); + if (ret < 0) { + mlog_errno(ret); + goto out; + } + } + + ocfs2_xattr_set_entry_normal(inode, xi, xs, name_hash, local); + + /*Only dirty the blocks we have touched in set xattr. */ + ret = ocfs2_xattr_bucket_handle_journal(inode, handle, xs, + xs->bucket.bhs, blk_per_bucket); + if (ret) + mlog_errno(ret); +out: + ocfs2_commit_trans(osb, handle); + + return ret; +} + +static int ocfs2_xattr_value_update_size(struct inode *inode, + struct buffer_head *xe_bh, + struct ocfs2_xattr_entry *xe, + u64 new_size) +{ + int ret; + struct ocfs2_super *osb = OCFS2_SB(inode->i_sb); + handle_t *handle = NULL; + + handle = ocfs2_start_trans(osb, 1); + if (handle == NULL) { + ret = -ENOMEM; + mlog_errno(ret); + goto out; + } + + ret = ocfs2_journal_access(handle, inode, xe_bh, + OCFS2_JOURNAL_ACCESS_WRITE); + if (ret < 0) { + mlog_errno(ret); + goto out_commit; + } + + xe->xe_value_size = cpu_to_le64(new_size); + + ret = ocfs2_journal_dirty(handle, xe_bh); + if (ret < 0) + mlog_errno(ret); + +out_commit: + ocfs2_commit_trans(osb, handle); +out: + return ret; +} + +/* + * Truncate the specified xe_off entry in xattr bucket. + * bucket is indicated by header_bh and len is the new length. + * Both the ocfs2_xattr_value_root and the entry will be updated here. + * + * Copy the new updated xe and xe_value_root to new_xe and new_xv if needed. + */ +static int ocfs2_xattr_bucket_value_truncate(struct inode *inode, + struct buffer_head *header_bh, + int xe_off, + int len) +{ + int ret, offset; + u64 value_blk; + struct buffer_head *value_bh = NULL; + struct ocfs2_xattr_value_root *xv; + struct ocfs2_xattr_entry *xe; + struct ocfs2_xattr_header *xh = + (struct ocfs2_xattr_header *)header_bh->b_data; + size_t blocksize = inode->i_sb->s_blocksize; + + xe = &xh->xh_entries[xe_off]; + + BUG_ON(!xe || ocfs2_xattr_is_local(xe)); + + offset = le16_to_cpu(xe->xe_name_offset) + + OCFS2_XATTR_SIZE(xe->xe_name_len); + + value_blk = offset / blocksize; + + /* We don't allow ocfs2_xattr_value to be stored in different block. */ + BUG_ON(value_blk != (offset + OCFS2_XATTR_ROOT_SIZE - 1) / blocksize); + value_blk += header_bh->b_blocknr; + + ret = ocfs2_read_block(inode, value_blk, &value_bh); + if (ret) { + mlog_errno(ret); + goto out; + } + + xv = (struct ocfs2_xattr_value_root *) + (value_bh->b_data + offset % blocksize); + + mlog(0, "truncate %u in xattr bucket %llu to %d bytes.\n", + xe_off, (unsigned long long)header_bh->b_blocknr, len); + ret = ocfs2_xattr_value_truncate(inode, value_bh, xv, len); + if (ret) { + mlog_errno(ret); + goto out; + } + + ret = ocfs2_xattr_value_update_size(inode, header_bh, xe, len); + if (ret) { + mlog_errno(ret); + goto out; + } + +out: + brelse(value_bh); + return ret; +} + +static int ocfs2_xattr_bucket_value_truncate_xs(struct inode *inode, + struct ocfs2_xattr_search *xs, + int len) +{ + int ret, offset; + struct ocfs2_xattr_entry *xe = xs->here; + struct ocfs2_xattr_header *xh = (struct ocfs2_xattr_header *)xs->base; + + BUG_ON(!xs->bucket.bhs[0] || !xe || ocfs2_xattr_is_local(xe)); + + offset = xe - xh->xh_entries; + ret = ocfs2_xattr_bucket_value_truncate(inode, xs->bucket.bhs[0], + offset, len); + if (ret) + mlog_errno(ret); + + return ret; +} + +static int ocfs2_xattr_bucket_set_value_outside(struct inode *inode, + struct ocfs2_xattr_search *xs, + char *val, + int value_len) +{ + int offset; + struct ocfs2_xattr_value_root *xv; + struct ocfs2_xattr_entry *xe = xs->here; + + BUG_ON(!xs->base || !xe || ocfs2_xattr_is_local(xe)); + + offset = le16_to_cpu(xe->xe_name_offset) + + OCFS2_XATTR_SIZE(xe->xe_name_len); + + xv = (struct ocfs2_xattr_value_root *)(xs->base + offset); + + return __ocfs2_xattr_set_value_outside(inode, xv, val, value_len); +} + +static int ocfs2_rm_xattr_cluster(struct inode *inode, + struct buffer_head *root_bh, + u64 blkno, + u32 cpos, + u32 len) +{ + int ret; + struct ocfs2_super *osb = OCFS2_SB(inode->i_sb); + struct inode *tl_inode = osb->osb_tl_inode; + handle_t *handle; + struct ocfs2_xattr_block *xb = + (struct ocfs2_xattr_block *)root_bh->b_data; + struct ocfs2_alloc_context *meta_ac = NULL; + struct ocfs2_cached_dealloc_ctxt dealloc; + struct ocfs2_extent_tree et; + + ocfs2_init_xattr_tree_extent_tree(&et, inode, root_bh); + + ocfs2_init_dealloc_ctxt(&dealloc); + + mlog(0, "rm xattr extent rec at %u len = %u, start from %llu\n", + cpos, len, (unsigned long long)blkno); + + ocfs2_remove_xattr_clusters_from_cache(inode, blkno, len); + + ret = ocfs2_lock_allocators(inode, &et, 0, 1, NULL, &meta_ac); + if (ret) { + mlog_errno(ret); + return ret; + } + + mutex_lock(&tl_inode->i_mutex); + + if (ocfs2_truncate_log_needs_flush(osb)) { + ret = __ocfs2_flush_truncate_log(osb); + if (ret < 0) { + mlog_errno(ret); + goto out; + } + } + + handle = ocfs2_start_trans(osb, OCFS2_REMOVE_EXTENT_CREDITS); + if (handle == NULL) { + ret = -ENOMEM; + mlog_errno(ret); + goto out; + } + + ret = ocfs2_journal_access(handle, inode, root_bh, + OCFS2_JOURNAL_ACCESS_WRITE); + if (ret) { + mlog_errno(ret); + goto out_commit; + } + + ret = ocfs2_remove_extent(inode, &et, cpos, len, handle, meta_ac, + &dealloc); + if (ret) { + mlog_errno(ret); + goto out_commit; + } + + le32_add_cpu(&xb->xb_attrs.xb_root.xt_clusters, -len); + + ret = ocfs2_journal_dirty(handle, root_bh); + if (ret) { + mlog_errno(ret); + goto out_commit; + } + + ret = ocfs2_truncate_log_append(osb, handle, blkno, len); + if (ret) + mlog_errno(ret); + +out_commit: + ocfs2_commit_trans(osb, handle); +out: + ocfs2_schedule_truncate_log_flush(osb, 1); + + mutex_unlock(&tl_inode->i_mutex); + + if (meta_ac) + ocfs2_free_alloc_context(meta_ac); + + ocfs2_run_deallocs(osb, &dealloc); + + return ret; +} + +static void ocfs2_xattr_bucket_remove_xs(struct inode *inode, + struct ocfs2_xattr_search *xs) +{ + handle_t *handle = NULL; + struct ocfs2_xattr_header *xh = xs->bucket.xh; + struct ocfs2_xattr_entry *last = &xh->xh_entries[ + le16_to_cpu(xh->xh_count) - 1]; + int ret = 0; + + handle = ocfs2_start_trans((OCFS2_SB(inode->i_sb)), 1); + if (IS_ERR(handle)) { + ret = PTR_ERR(handle); + mlog_errno(ret); + return; + } + + ret = ocfs2_journal_access(handle, inode, xs->bucket.bhs[0], + OCFS2_JOURNAL_ACCESS_WRITE); + if (ret) { + mlog_errno(ret); + goto out_commit; + } + + /* Remove the old entry. */ + memmove(xs->here, xs->here + 1, + (void *)last - (void *)xs->here); + memset(last, 0, sizeof(struct ocfs2_xattr_entry)); + le16_add_cpu(&xh->xh_count, -1); + + ret = ocfs2_journal_dirty(handle, xs->bucket.bhs[0]); + if (ret < 0) + mlog_errno(ret); +out_commit: + ocfs2_commit_trans(OCFS2_SB(inode->i_sb), handle); +} + +/* + * Set the xattr name/value in the bucket specified in xs. + * + * As the new value in xi may be stored in the bucket or in an outside cluster, + * we divide the whole process into 3 steps: + * 1. insert name/value in the bucket(ocfs2_xattr_set_entry_in_bucket) + * 2. truncate of the outside cluster(ocfs2_xattr_bucket_value_truncate_xs) + * 3. Set the value to the outside cluster(ocfs2_xattr_bucket_set_value_outside) + * 4. If the clusters for the new outside value can't be allocated, we need + * to free the xattr we allocated in set. + */ +static int ocfs2_xattr_set_in_bucket(struct inode *inode, + struct ocfs2_xattr_info *xi, + struct ocfs2_xattr_search *xs) +{ + int ret, local = 1; + size_t value_len; + char *val = (char *)xi->value; + struct ocfs2_xattr_entry *xe = xs->here; + u32 name_hash = ocfs2_xattr_name_hash(inode, xi->name, + strlen(xi->name)); + + if (!xs->not_found && !ocfs2_xattr_is_local(xe)) { + /* + * We need to truncate the xattr storage first. + * + * If both the old and new value are stored to + * outside block, we only need to truncate + * the storage and then set the value outside. + * + * If the new value should be stored within block, + * we should free all the outside block first and + * the modification to the xattr block will be done + * by following steps. + */ + if (xi->value_len > OCFS2_XATTR_INLINE_SIZE) + value_len = xi->value_len; + else + value_len = 0; + + ret = ocfs2_xattr_bucket_value_truncate_xs(inode, xs, + value_len); + if (ret) + goto out; + + if (value_len) + goto set_value_outside; + } + + value_len = xi->value_len; + /* So we have to handle the inside block change now. */ + if (value_len > OCFS2_XATTR_INLINE_SIZE) { + /* + * If the new value will be stored outside of block, + * initalize a new empty value root and insert it first. + */ + local = 0; + xi->value = &def_xv; + xi->value_len = OCFS2_XATTR_ROOT_SIZE; + } + + ret = ocfs2_xattr_set_entry_in_bucket(inode, xi, xs, name_hash, local); + if (ret) { + mlog_errno(ret); + goto out; + } + + if (value_len <= OCFS2_XATTR_INLINE_SIZE) + goto out; + + /* allocate the space now for the outside block storage. */ + ret = ocfs2_xattr_bucket_value_truncate_xs(inode, xs, + value_len); + if (ret) { + mlog_errno(ret); + + if (xs->not_found) { + /* + * We can't allocate enough clusters for outside + * storage and we have allocated xattr already, + * so need to remove it. + */ + ocfs2_xattr_bucket_remove_xs(inode, xs); + } + goto out; + } + +set_value_outside: + ret = ocfs2_xattr_bucket_set_value_outside(inode, xs, val, value_len); +out: + return ret; +} + +/* check whether the xattr bucket is filled up with the same hash value. */ +static int ocfs2_check_xattr_bucket_collision(struct inode *inode, + struct ocfs2_xattr_bucket *bucket) +{ + struct ocfs2_xattr_header *xh = bucket->xh; + + if (xh->xh_entries[le16_to_cpu(xh->xh_count) - 1].xe_name_hash == + xh->xh_entries[0].xe_name_hash) { + mlog(ML_ERROR, "Too much hash collision in xattr bucket %llu, " + "hash = %u\n", + (unsigned long long)bucket->bhs[0]->b_blocknr, + le32_to_cpu(xh->xh_entries[0].xe_name_hash)); + return -ENOSPC; + } + + return 0; +} + +static int ocfs2_xattr_set_entry_index_block(struct inode *inode, + struct ocfs2_xattr_info *xi, + struct ocfs2_xattr_search *xs) +{ + struct ocfs2_xattr_header *xh; + struct ocfs2_xattr_entry *xe; + u16 count, header_size, xh_free_start; + int i, free, max_free, need, old; + size_t value_size = 0, name_len = strlen(xi->name); + size_t blocksize = inode->i_sb->s_blocksize; + int ret, allocation = 0; + u16 blk_per_bucket = ocfs2_blocks_per_xattr_bucket(inode->i_sb); + + mlog_entry("Set xattr %s in xattr index block\n", xi->name); + +try_again: + xh = xs->header; + count = le16_to_cpu(xh->xh_count); + xh_free_start = le16_to_cpu(xh->xh_free_start); + header_size = sizeof(struct ocfs2_xattr_header) + + count * sizeof(struct ocfs2_xattr_entry); + max_free = OCFS2_XATTR_BUCKET_SIZE - + le16_to_cpu(xh->xh_name_value_len) - header_size; + + mlog_bug_on_msg(header_size > blocksize, "bucket %llu has header size " + "of %u which exceed block size\n", + (unsigned long long)xs->bucket.bhs[0]->b_blocknr, + header_size); + + if (xi->value && xi->value_len > OCFS2_XATTR_INLINE_SIZE) + value_size = OCFS2_XATTR_ROOT_SIZE; + else if (xi->value) + value_size = OCFS2_XATTR_SIZE(xi->value_len); + + if (xs->not_found) + need = sizeof(struct ocfs2_xattr_entry) + + OCFS2_XATTR_SIZE(name_len) + value_size; + else { + need = value_size + OCFS2_XATTR_SIZE(name_len); + + /* + * We only replace the old value if the new length is smaller + * than the old one. Otherwise we will allocate new space in the + * bucket to store it. + */ + xe = xs->here; + if (ocfs2_xattr_is_local(xe)) + old = OCFS2_XATTR_SIZE(le64_to_cpu(xe->xe_value_size)); + else + old = OCFS2_XATTR_SIZE(OCFS2_XATTR_ROOT_SIZE); + + if (old >= value_size) + need = 0; + } + + free = xh_free_start - header_size; + /* + * We need to make sure the new name/value pair + * can exist in the same block. + */ + if (xh_free_start % blocksize < need) + free -= xh_free_start % blocksize; + + mlog(0, "xs->not_found = %d, in xattr bucket %llu: free = %d, " + "need = %d, max_free = %d, xh_free_start = %u, xh_name_value_len =" + " %u\n", xs->not_found, + (unsigned long long)xs->bucket.bhs[0]->b_blocknr, + free, need, max_free, le16_to_cpu(xh->xh_free_start), + le16_to_cpu(xh->xh_name_value_len)); + + if (free < need || count == ocfs2_xattr_max_xe_in_bucket(inode->i_sb)) { + if (need <= max_free && + count < ocfs2_xattr_max_xe_in_bucket(inode->i_sb)) { + /* + * We can create the space by defragment. Since only the + * name/value will be moved, the xe shouldn't be changed + * in xs. + */ + ret = ocfs2_defrag_xattr_bucket(inode, &xs->bucket); + if (ret) { + mlog_errno(ret); + goto out; + } + + xh_free_start = le16_to_cpu(xh->xh_free_start); + free = xh_free_start - header_size; + if (xh_free_start % blocksize < need) + free -= xh_free_start % blocksize; + + if (free >= need) + goto xattr_set; + + mlog(0, "Can't get enough space for xattr insert by " + "defragment. Need %u bytes, but we have %d, so " + "allocate new bucket for it.\n", need, free); + } + + /* + * We have to add new buckets or clusters and one + * allocation should leave us enough space for insert. + */ + BUG_ON(allocation); + + /* + * We do not allow for overlapping ranges between buckets. And + * the maximum number of collisions we will allow for then is + * one bucket's worth, so check it here whether we need to + * add a new bucket for the insert. + */ + ret = ocfs2_check_xattr_bucket_collision(inode, &xs->bucket); + if (ret) { + mlog_errno(ret); + goto out; + } + + ret = ocfs2_add_new_xattr_bucket(inode, + xs->xattr_bh, + xs->bucket.bhs[0]); + if (ret) { + mlog_errno(ret); + goto out; + } + + for (i = 0; i < blk_per_bucket; i++) + brelse(xs->bucket.bhs[i]); + + memset(&xs->bucket, 0, sizeof(xs->bucket)); + + ret = ocfs2_xattr_index_block_find(inode, xs->xattr_bh, + xi->name_index, + xi->name, xs); + if (ret && ret != -ENODATA) + goto out; + xs->not_found = ret; + allocation = 1; + goto try_again; + } + +xattr_set: + ret = ocfs2_xattr_set_in_bucket(inode, xi, xs); +out: + mlog_exit(ret); + return ret; +} + +static int ocfs2_delete_xattr_in_bucket(struct inode *inode, + struct ocfs2_xattr_bucket *bucket, + void *para) +{ + int ret = 0; + struct ocfs2_xattr_header *xh = bucket->xh; + u16 i; + struct ocfs2_xattr_entry *xe; + + for (i = 0; i < le16_to_cpu(xh->xh_count); i++) { + xe = &xh->xh_entries[i]; + if (ocfs2_xattr_is_local(xe)) + continue; + + ret = ocfs2_xattr_bucket_value_truncate(inode, + bucket->bhs[0], + i, 0); + if (ret) { + mlog_errno(ret); + break; + } + } + + return ret; +} + +static int ocfs2_delete_xattr_index_block(struct inode *inode, + struct buffer_head *xb_bh) +{ + struct ocfs2_xattr_block *xb = + (struct ocfs2_xattr_block *)xb_bh->b_data; + struct ocfs2_extent_list *el = &xb->xb_attrs.xb_root.xt_list; + int ret = 0; + u32 name_hash = UINT_MAX, e_cpos, num_clusters; + u64 p_blkno; + + if (le16_to_cpu(el->l_next_free_rec) == 0) + return 0; + + while (name_hash > 0) { + ret = ocfs2_xattr_get_rec(inode, name_hash, &p_blkno, + &e_cpos, &num_clusters, el); + if (ret) { + mlog_errno(ret); + goto out; + } + + ret = ocfs2_iterate_xattr_buckets(inode, p_blkno, num_clusters, + ocfs2_delete_xattr_in_bucket, + NULL); + if (ret) { + mlog_errno(ret); + goto out; + } + + ret = ocfs2_rm_xattr_cluster(inode, xb_bh, + p_blkno, e_cpos, num_clusters); + if (ret) { + mlog_errno(ret); + break; + } + + if (e_cpos == 0) + break; + + name_hash = e_cpos - 1; + } + +out: + return ret; +} + +/* + * 'trusted' attributes support + */ + +#define XATTR_TRUSTED_PREFIX "trusted." + +static size_t ocfs2_xattr_trusted_list(struct inode *inode, char *list, + size_t list_size, const char *name, + size_t name_len) +{ + const size_t prefix_len = sizeof(XATTR_TRUSTED_PREFIX) - 1; + const size_t total_len = prefix_len + name_len + 1; + + if (list && total_len <= list_size) { + memcpy(list, XATTR_TRUSTED_PREFIX, prefix_len); + memcpy(list + prefix_len, name, name_len); + list[prefix_len + name_len] = '\0'; + } + return total_len; +} + +static int ocfs2_xattr_trusted_get(struct inode *inode, const char *name, + void *buffer, size_t size) +{ + if (strcmp(name, "") == 0) + return -EINVAL; + return ocfs2_xattr_get(inode, OCFS2_XATTR_INDEX_TRUSTED, name, + buffer, size); +} + +static int ocfs2_xattr_trusted_set(struct inode *inode, const char *name, + const void *value, size_t size, int flags) +{ + if (strcmp(name, "") == 0) + return -EINVAL; + + return ocfs2_xattr_set(inode, OCFS2_XATTR_INDEX_TRUSTED, name, value, + size, flags); +} + +struct xattr_handler ocfs2_xattr_trusted_handler = { + .prefix = XATTR_TRUSTED_PREFIX, + .list = ocfs2_xattr_trusted_list, + .get = ocfs2_xattr_trusted_get, + .set = ocfs2_xattr_trusted_set, +}; + + +/* + * 'user' attributes support + */ + +#define XATTR_USER_PREFIX "user." + +static size_t ocfs2_xattr_user_list(struct inode *inode, char *list, + size_t list_size, const char *name, + size_t name_len) +{ + const size_t prefix_len = sizeof(XATTR_USER_PREFIX) - 1; + const size_t total_len = prefix_len + name_len + 1; + struct ocfs2_super *osb = OCFS2_SB(inode->i_sb); + + if (osb->s_mount_opt & OCFS2_MOUNT_NOUSERXATTR) + return 0; + + if (list && total_len <= list_size) { + memcpy(list, XATTR_USER_PREFIX, prefix_len); + memcpy(list + prefix_len, name, name_len); + list[prefix_len + name_len] = '\0'; + } + return total_len; +} + +static int ocfs2_xattr_user_get(struct inode *inode, const char *name, + void *buffer, size_t size) +{ + struct ocfs2_super *osb = OCFS2_SB(inode->i_sb); + + if (strcmp(name, "") == 0) + return -EINVAL; + if (osb->s_mount_opt & OCFS2_MOUNT_NOUSERXATTR) + return -EOPNOTSUPP; + return ocfs2_xattr_get(inode, OCFS2_XATTR_INDEX_USER, name, + buffer, size); +} + +static int ocfs2_xattr_user_set(struct inode *inode, const char *name, + const void *value, size_t size, int flags) +{ + struct ocfs2_super *osb = OCFS2_SB(inode->i_sb); + + if (strcmp(name, "") == 0) + return -EINVAL; + if (osb->s_mount_opt & OCFS2_MOUNT_NOUSERXATTR) + return -EOPNOTSUPP; + + return ocfs2_xattr_set(inode, OCFS2_XATTR_INDEX_USER, name, value, + size, flags); +} + +struct xattr_handler ocfs2_xattr_user_handler = { + .prefix = XATTR_USER_PREFIX, + .list = ocfs2_xattr_user_list, + .get = ocfs2_xattr_user_get, + .set = ocfs2_xattr_user_set, +}; diff --git a/fs/ocfs2/xattr.h b/fs/ocfs2/xattr.h new file mode 100644 index 000000000000..c25c7c62a059 --- /dev/null +++ b/fs/ocfs2/xattr.h @@ -0,0 +1,68 @@ +/* -*- mode: c; c-basic-offset: 8; -*- + * vim: noexpandtab sw=8 ts=8 sts=0: + * + * xattr.h + * + * Function prototypes + * + * Copyright (C) 2008 Oracle. All rights reserved. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public + * License along with this program; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 021110-1307, USA. + */ + +#ifndef OCFS2_XATTR_H +#define OCFS2_XATTR_H + +#include <linux/init.h> +#include <linux/xattr.h> + +enum ocfs2_xattr_type { + OCFS2_XATTR_INDEX_USER = 1, + OCFS2_XATTR_INDEX_POSIX_ACL_ACCESS, + OCFS2_XATTR_INDEX_POSIX_ACL_DEFAULT, + OCFS2_XATTR_INDEX_TRUSTED, + OCFS2_XATTR_INDEX_SECURITY, + OCFS2_XATTR_MAX +}; + +extern struct xattr_handler ocfs2_xattr_user_handler; +extern struct xattr_handler ocfs2_xattr_trusted_handler; + +extern ssize_t ocfs2_listxattr(struct dentry *, char *, size_t); +extern int ocfs2_xattr_get(struct inode *, int, const char *, void *, size_t); +extern int ocfs2_xattr_set(struct inode *, int, const char *, const void *, + size_t, int); +extern int ocfs2_xattr_remove(struct inode *inode, struct buffer_head *di_bh); +extern struct xattr_handler *ocfs2_xattr_handlers[]; + +static inline u16 ocfs2_xattr_buckets_per_cluster(struct ocfs2_super *osb) +{ + return (1 << osb->s_clustersize_bits) / OCFS2_XATTR_BUCKET_SIZE; +} + +static inline u16 ocfs2_blocks_per_xattr_bucket(struct super_block *sb) +{ + return OCFS2_XATTR_BUCKET_SIZE / (1 << sb->s_blocksize_bits); +} + +static inline u16 ocfs2_xattr_max_xe_in_bucket(struct super_block *sb) +{ + u16 len = sb->s_blocksize - + offsetof(struct ocfs2_xattr_header, xh_entries); + + return len / sizeof(struct ocfs2_xattr_entry); +} +#endif /* OCFS2_XATTR_H */ diff --git a/fs/proc/proc_misc.c b/fs/proc/proc_misc.c index 66c1ab87656c..b675a49c1823 100644 --- a/fs/proc/proc_misc.c +++ b/fs/proc/proc_misc.c @@ -683,6 +683,7 @@ static int cmdline_read_proc(char *page, char **start, off_t off, return proc_calc_metrics(page, start, off, count, eof, len); } +#ifdef CONFIG_FILE_LOCKING static int locks_open(struct inode *inode, struct file *filp) { return seq_open(filp, &locks_seq_operations); @@ -694,6 +695,7 @@ static const struct file_operations proc_locks_operations = { .llseek = seq_lseek, .release = seq_release, }; +#endif /* CONFIG_FILE_LOCKING */ static int execdomains_read_proc(char *page, char **start, off_t off, int count, int *eof, void *data) @@ -887,7 +889,9 @@ void __init proc_misc_init(void) #ifdef CONFIG_PRINTK proc_create("kmsg", S_IRUSR, NULL, &proc_kmsg_operations); #endif +#ifdef CONFIG_FILE_LOCKING proc_create("locks", 0, NULL, &proc_locks_operations); +#endif proc_create("devices", 0, NULL, &proc_devinfo_operations); proc_create("cpuinfo", 0, NULL, &proc_cpuinfo_operations); #ifdef CONFIG_BLOCK diff --git a/include/asm-m68k/atarihw.h b/include/asm-m68k/atarihw.h index ecf007df7743..1412b4ab202f 100644 --- a/include/asm-m68k/atarihw.h +++ b/include/asm-m68k/atarihw.h @@ -39,7 +39,6 @@ extern int atari_dont_touch_floppy_select; #define MACH_IS_TT ((atari_mch_cookie >> 16) == ATARI_MCH_TT) #define MACH_IS_FALCON ((atari_mch_cookie >> 16) == ATARI_MCH_FALCON) #define MACH_IS_MEDUSA (atari_mch_type == ATARI_MACH_MEDUSA) -#define MACH_IS_HADES (atari_mch_type == ATARI_MACH_HADES) #define MACH_IS_AB40 (atari_mch_type == ATARI_MACH_AB40) /* values for atari_switches */ diff --git a/include/asm-m68k/dma-mapping.h b/include/asm-m68k/dma-mapping.h index 91f7944333d4..26f505488c11 100644 --- a/include/asm-m68k/dma-mapping.h +++ b/include/asm-m68k/dma-mapping.h @@ -74,6 +74,14 @@ extern void dma_sync_single_for_device(struct device *, dma_addr_t, size_t, extern void dma_sync_sg_for_device(struct device *, struct scatterlist *, int, enum dma_data_direction); +static inline void dma_sync_single_range_for_device(struct device *dev, + dma_addr_t dma_handle, unsigned long offset, size_t size, + enum dma_data_direction direction) +{ + /* just sync everything for now */ + dma_sync_single_for_device(dev, dma_handle, offset + size, direction); +} + static inline void dma_sync_single_for_cpu(struct device *dev, dma_addr_t handle, size_t size, enum dma_data_direction dir) { @@ -84,6 +92,14 @@ static inline void dma_sync_sg_for_cpu(struct device *dev, struct scatterlist *s { } +static inline void dma_sync_single_range_for_cpu(struct device *dev, + dma_addr_t dma_handle, unsigned long offset, size_t size, + enum dma_data_direction direction) +{ + /* just sync everything for now */ + dma_sync_single_for_cpu(dev, dma_handle, offset + size, direction); +} + static inline int dma_mapping_error(struct device *dev, dma_addr_t handle) { return 0; diff --git a/include/asm-m68k/dma.h b/include/asm-m68k/dma.h index d0c9e61e57b4..4240fbc946f8 100644 --- a/include/asm-m68k/dma.h +++ b/include/asm-m68k/dma.h @@ -11,10 +11,6 @@ extern int request_dma(unsigned int dmanr, const char * device_id); /* reserve a DMA channel */ extern void free_dma(unsigned int dmanr); /* release it again */ -#ifdef CONFIG_PCI -extern int isa_dma_bridge_buggy; -#else #define isa_dma_bridge_buggy (0) -#endif #endif /* _M68K_DMA_H */ diff --git a/include/asm-m68k/entry.h b/include/asm-m68k/entry.h index f8f6b185d793..5202f5a5b420 100644 --- a/include/asm-m68k/entry.h +++ b/include/asm-m68k/entry.h @@ -31,7 +31,7 @@ */ /* the following macro is used when enabling interrupts */ -#if defined(MACH_ATARI_ONLY) && !defined(CONFIG_HADES) +#if defined(MACH_ATARI_ONLY) /* block out HSYNC on the atari */ #define ALLOWINT (~0x400) #define MAX_NOINT_IPL 3 diff --git a/include/asm-m68k/io.h b/include/asm-m68k/io.h index 657187f0c7c2..9e673e3bd434 100644 --- a/include/asm-m68k/io.h +++ b/include/asm-m68k/io.h @@ -7,15 +7,12 @@ * - added skeleton for GG-II and Amiga PCMCIA * 2/3/01 RZ: - moved a few more defs into raw_io.h * - * inX/outX/readX/writeX should not be used by any driver unless it does - * ISA or PCI access. Other drivers should use function defined in raw_io.h + * inX/outX should not be used by any driver unless it does + * ISA access. Other drivers should use function defined in raw_io.h * or define its own macros on top of these. * - * inX(),outX() are for PCI and ISA I/O - * readX(),writeX() are for PCI memory + * inX(),outX() are for ISA I/O * isa_readX(),isa_writeX() are for ISA memory - * - * moved mem{cpy,set}_*io inside CONFIG_PCI */ #ifndef _IO_H @@ -256,10 +253,7 @@ static inline void isa_delay(void) (ISA_SEX ? raw_outsl(isa_itl(port), (u32 *)(buf), (nr)) : \ raw_outsw_swapw(isa_itw(port), (u16 *)(buf), (nr)<<1)) -#endif /* CONFIG_ISA */ - -#if defined(CONFIG_ISA) && !defined(CONFIG_PCI) #define inb isa_inb #define inb_p isa_inb_p #define outb isa_outb @@ -282,55 +276,9 @@ static inline void isa_delay(void) #define readw isa_readw #define writeb isa_writeb #define writew isa_writew -#endif /* CONFIG_ISA */ - -#if defined(CONFIG_PCI) - -#define readl(addr) in_le32(addr) -#define writel(val,addr) out_le32((addr),(val)) - -/* those can be defined for both ISA and PCI - it won't work though */ -#define readb(addr) in_8(addr) -#define readw(addr) in_le16(addr) -#define writeb(val,addr) out_8((addr),(val)) -#define writew(val,addr) out_le16((addr),(val)) -#define readb_relaxed(addr) readb(addr) -#define readw_relaxed(addr) readw(addr) -#define readl_relaxed(addr) readl(addr) +#else /* CONFIG_ISA */ -#ifndef CONFIG_ISA -#define inb(port) in_8(port) -#define outb(val,port) out_8((port),(val)) -#define inw(port) in_le16(port) -#define outw(val,port) out_le16((port),(val)) -#define inl(port) in_le32(port) -#define outl(val,port) out_le32((port),(val)) - -#else -/* - * kernel with both ISA and PCI compiled in, those have - * conflicting defs for in/out. Simply consider port < 1024 - * ISA and everything else PCI. read,write not defined - * in this case - */ -#define inb(port) ((port)<1024 ? isa_inb(port) : in_8(port)) -#define inb_p(port) ((port)<1024 ? isa_inb_p(port) : in_8(port)) -#define inw(port) ((port)<1024 ? isa_inw(port) : in_le16(port)) -#define inw_p(port) ((port)<1024 ? isa_inw_p(port) : in_le16(port)) -#define inl(port) ((port)<1024 ? isa_inl(port) : in_le32(port)) -#define inl_p(port) ((port)<1024 ? isa_inl_p(port) : in_le32(port)) - -#define outb(val,port) ((port)<1024 ? isa_outb((val),(port)) : out_8((port),(val))) -#define outb_p(val,port) ((port)<1024 ? isa_outb_p((val),(port)) : out_8((port),(val))) -#define outw(val,port) ((port)<1024 ? isa_outw((val),(port)) : out_le16((port),(val))) -#define outw_p(val,port) ((port)<1024 ? isa_outw_p((val),(port)) : out_le16((port),(val))) -#define outl(val,port) ((port)<1024 ? isa_outl((val),(port)) : out_le32((port),(val))) -#define outl_p(val,port) ((port)<1024 ? isa_outl_p((val),(port)) : out_le32((port),(val))) -#endif -#endif /* CONFIG_PCI */ - -#if !defined(CONFIG_ISA) && !defined(CONFIG_PCI) /* * We need to define dummy functions for GENERIC_IOMAP support. */ @@ -357,11 +305,11 @@ static inline void isa_delay(void) #define writeb(val,addr) out_8((addr),(val)) #define readw(addr) in_le16(addr) #define writew(val,addr) out_le16((addr),(val)) -#endif -#if !defined(CONFIG_PCI) + +#endif /* CONFIG_ISA */ + #define readl(addr) in_le32(addr) #define writel(val,addr) out_le32((addr),(val)) -#endif #define mmiowb() diff --git a/include/asm-m68k/pci.h b/include/asm-m68k/pci.h index 678cb0b52314..4ad0aea48ab4 100644 --- a/include/asm-m68k/pci.h +++ b/include/asm-m68k/pci.h @@ -1,52 +1,7 @@ #ifndef _ASM_M68K_PCI_H #define _ASM_M68K_PCI_H -/* - * asm-m68k/pci_m68k.h - m68k specific PCI declarations. - * - * Written by Wout Klaren. - */ - -#include <asm/scatterlist.h> - -struct pci_ops; - -/* - * Structure with hardware dependent information and functions of the - * PCI bus. - */ - -struct pci_bus_info -{ - /* - * Resources of the PCI bus. - */ - - struct resource mem_space; - struct resource io_space; - - /* - * System dependent functions. - */ - - struct pci_ops *m68k_pci_ops; - - void (*fixup)(int pci_modify); - void (*conf_device)(struct pci_dev *dev); -}; - -#define pcibios_assign_all_busses() 0 -#define pcibios_scan_all_fns(a, b) 0 - -static inline void pcibios_set_master(struct pci_dev *dev) -{ - /* No special bus mastering setup handling */ -} - -static inline void pcibios_penalize_isa_irq(int irq, int active) -{ - /* We don't do dynamic PCI IRQ allocation */ -} +#include <asm-generic/pci-dma-compat.h> /* The PCI address space does equal the physical memory * address space. The networking and block device layers use diff --git a/include/asm-m68k/virtconvert.h b/include/asm-m68k/virtconvert.h index dea32fbc7e51..22ab05c9c52b 100644 --- a/include/asm-m68k/virtconvert.h +++ b/include/asm-m68k/virtconvert.h @@ -40,15 +40,9 @@ static inline void *phys_to_virt(unsigned long address) /* * IO bus memory addresses are 1:1 with the physical address, - * except on the PCI bus of the Hades. */ -#ifdef CONFIG_HADES -#define virt_to_bus(a) (virt_to_phys(a) + (MACH_IS_HADES ? 0x80000000 : 0)) -#define bus_to_virt(a) (phys_to_virt((a) - (MACH_IS_HADES ? 0x80000000 : 0))) -#else #define virt_to_bus virt_to_phys #define bus_to_virt phys_to_virt -#endif #endif #endif diff --git a/include/linux/console_struct.h b/include/linux/console_struct.h index b03f80a078be..d71f7c0f931b 100644 --- a/include/linux/console_struct.h +++ b/include/linux/console_struct.h @@ -53,7 +53,6 @@ struct vc_data { unsigned short vc_hi_font_mask; /* [#] Attribute set for upper 256 chars of font or 0 if not supported */ struct console_font vc_font; /* Current VC font set */ unsigned short vc_video_erase_char; /* Background erase character */ - unsigned short vc_scrl_erase_char; /* Erase character for scroll */ /* VT terminal data */ unsigned int vc_state; /* Escape sequence parser state */ unsigned int vc_npar,vc_par[NPAR]; /* Parameters of current escape sequence */ diff --git a/include/linux/fs.h b/include/linux/fs.h index 44e3cb2f1966..a6a625be13fc 100644 --- a/include/linux/fs.h +++ b/include/linux/fs.h @@ -947,6 +947,14 @@ struct lock_manager_operations { int (*fl_change)(struct file_lock **, int); }; +struct lock_manager { + struct list_head list; +}; + +void locks_start_grace(struct lock_manager *); +void locks_end_grace(struct lock_manager *); +int locks_in_grace(void); + /* that will die - we need it for nfs_lock_info */ #include <linux/nfs_fs_i.h> @@ -988,6 +996,13 @@ struct file_lock { #include <linux/fcntl.h> +extern void send_sigio(struct fown_struct *fown, int fd, int band); + +/* fs/sync.c */ +extern int do_sync_mapping_range(struct address_space *mapping, loff_t offset, + loff_t endbyte, unsigned int flags); + +#ifdef CONFIG_FILE_LOCKING extern int fcntl_getlk(struct file *, struct flock __user *); extern int fcntl_setlk(unsigned int, struct file *, unsigned int, struct flock __user *); @@ -998,14 +1013,9 @@ extern int fcntl_setlk64(unsigned int, struct file *, unsigned int, struct flock64 __user *); #endif -extern void send_sigio(struct fown_struct *fown, int fd, int band); extern int fcntl_setlease(unsigned int fd, struct file *filp, long arg); extern int fcntl_getlease(struct file *filp); -/* fs/sync.c */ -extern int do_sync_mapping_range(struct address_space *mapping, loff_t offset, - loff_t endbyte, unsigned int flags); - /* fs/locks.c */ extern void locks_init_lock(struct file_lock *); extern void locks_copy_lock(struct file_lock *, struct file_lock *); @@ -1028,6 +1038,37 @@ extern int lease_modify(struct file_lock **, int); extern int lock_may_read(struct inode *, loff_t start, unsigned long count); extern int lock_may_write(struct inode *, loff_t start, unsigned long count); extern struct seq_operations locks_seq_operations; +#else /* !CONFIG_FILE_LOCKING */ +#define fcntl_getlk(a, b) ({ -EINVAL; }) +#define fcntl_setlk(a, b, c, d) ({ -EACCES; }) +#if BITS_PER_LONG == 32 +#define fcntl_getlk64(a, b) ({ -EINVAL; }) +#define fcntl_setlk64(a, b, c, d) ({ -EACCES; }) +#endif +#define fcntl_setlease(a, b, c) ({ 0; }) +#define fcntl_getlease(a) ({ 0; }) +#define locks_init_lock(a) ({ }) +#define __locks_copy_lock(a, b) ({ }) +#define locks_copy_lock(a, b) ({ }) +#define locks_remove_posix(a, b) ({ }) +#define locks_remove_flock(a) ({ }) +#define posix_test_lock(a, b) ({ 0; }) +#define posix_lock_file(a, b, c) ({ -ENOLCK; }) +#define posix_lock_file_wait(a, b) ({ -ENOLCK; }) +#define posix_unblock_lock(a, b) (-ENOENT) +#define vfs_test_lock(a, b) ({ 0; }) +#define vfs_lock_file(a, b, c, d) (-ENOLCK) +#define vfs_cancel_lock(a, b) ({ 0; }) +#define flock_lock_file_wait(a, b) ({ -ENOLCK; }) +#define __break_lease(a, b) ({ 0; }) +#define lease_get_mtime(a, b) ({ }) +#define generic_setlease(a, b, c) ({ -EINVAL; }) +#define vfs_setlease(a, b, c) ({ -EINVAL; }) +#define lease_modify(a, b) ({ -EINVAL; }) +#define lock_may_read(a, b, c) ({ 1; }) +#define lock_may_write(a, b, c) ({ 1; }) +#endif /* !CONFIG_FILE_LOCKING */ + struct fasync_struct { int magic; @@ -1575,9 +1616,12 @@ extern int vfs_statfs(struct dentry *, struct kstatfs *); /* /sys/fs */ extern struct kobject *fs_kobj; +extern int rw_verify_area(int, struct file *, loff_t *, size_t); + #define FLOCK_VERIFY_READ 1 #define FLOCK_VERIFY_WRITE 2 +#ifdef CONFIG_FILE_LOCKING extern int locks_mandatory_locked(struct inode *); extern int locks_mandatory_area(int, struct inode *, struct file *, loff_t, size_t); @@ -1608,8 +1652,6 @@ static inline int locks_verify_locked(struct inode *inode) return 0; } -extern int rw_verify_area(int, struct file *, loff_t *, size_t); - static inline int locks_verify_truncate(struct inode *inode, struct file *filp, loff_t size) @@ -1630,6 +1672,15 @@ static inline int break_lease(struct inode *inode, unsigned int mode) return __break_lease(inode, mode); return 0; } +#else /* !CONFIG_FILE_LOCKING */ +#define locks_mandatory_locked(a) ({ 0; }) +#define locks_mandatory_area(a, b, c, d, e) ({ 0; }) +#define __mandatory_lock(a) ({ 0; }) +#define mandatory_lock(a) ({ 0; }) +#define locks_verify_locked(a) ({ 0; }) +#define locks_verify_truncate(a, b, c) ({ 0; }) +#define break_lease(a, b) ({ 0; }) +#endif /* CONFIG_FILE_LOCKING */ /* fs/open.c */ diff --git a/include/linux/hid.h b/include/linux/hid.h index ac4e678a04ed..f13bca2dd53b 100644 --- a/include/linux/hid.h +++ b/include/linux/hid.h @@ -67,6 +67,7 @@ #include <linux/types.h> #include <linux/slab.h> #include <linux/list.h> +#include <linux/mod_devicetable.h> /* hid_device_id */ #include <linux/timer.h> #include <linux/workqueue.h> #include <linux/input.h> @@ -246,6 +247,19 @@ struct hid_item { #define HID_FEATURE_REPORT 2 /* + * HID connect requests + */ + +#define HID_CONNECT_HIDINPUT 0x01 +#define HID_CONNECT_HIDINPUT_FORCE 0x02 +#define HID_CONNECT_HIDRAW 0x04 +#define HID_CONNECT_HIDDEV 0x08 +#define HID_CONNECT_HIDDEV_FORCE 0x10 +#define HID_CONNECT_FF 0x20 +#define HID_CONNECT_DEFAULT (HID_CONNECT_HIDINPUT|HID_CONNECT_HIDRAW| \ + HID_CONNECT_HIDDEV|HID_CONNECT_FF) + +/* * HID device quirks. */ @@ -256,48 +270,11 @@ struct hid_item { #define HID_QUIRK_INVERT 0x00000001 #define HID_QUIRK_NOTOUCH 0x00000002 -#define HID_QUIRK_IGNORE 0x00000004 #define HID_QUIRK_NOGET 0x00000008 -#define HID_QUIRK_HIDDEV 0x00000010 #define HID_QUIRK_BADPAD 0x00000020 #define HID_QUIRK_MULTI_INPUT 0x00000040 -#define HID_QUIRK_2WHEEL_MOUSE_HACK_7 0x00000080 -#define HID_QUIRK_2WHEEL_MOUSE_HACK_5 0x00000100 -#define HID_QUIRK_2WHEEL_MOUSE_HACK_ON 0x00000200 -#define HID_QUIRK_MIGHTYMOUSE 0x00000400 -#define HID_QUIRK_APPLE_HAS_FN 0x00000800 -#define HID_QUIRK_APPLE_FN_ON 0x00001000 -#define HID_QUIRK_INVERT_HWHEEL 0x00002000 -#define HID_QUIRK_APPLE_ISO_KEYBOARD 0x00004000 -#define HID_QUIRK_BAD_RELATIVE_KEYS 0x00008000 #define HID_QUIRK_SKIP_OUTPUT_REPORTS 0x00010000 -#define HID_QUIRK_IGNORE_MOUSE 0x00020000 -#define HID_QUIRK_SONY_PS3_CONTROLLER 0x00040000 -#define HID_QUIRK_DUPLICATE_USAGES 0x00080000 -#define HID_QUIRK_RESET_LEDS 0x00100000 -#define HID_QUIRK_HIDINPUT 0x00200000 -#define HID_QUIRK_LOGITECH_IGNORE_DOUBLED_WHEEL 0x00400000 -#define HID_QUIRK_LOGITECH_EXPANDED_KEYMAP 0x00800000 -#define HID_QUIRK_IGNORE_HIDINPUT 0x01000000 -#define HID_QUIRK_2WHEEL_MOUSE_HACK_B8 0x02000000 -#define HID_QUIRK_HWHEEL_WHEEL_INVERT 0x04000000 -#define HID_QUIRK_MICROSOFT_KEYS 0x08000000 #define HID_QUIRK_FULLSPEED_INTERVAL 0x10000000 -#define HID_QUIRK_APPLE_NUMLOCK_EMULATION 0x20000000 - -/* - * Separate quirks for runtime report descriptor fixup - */ - -#define HID_QUIRK_RDESC_CYMOTION 0x00000001 -#define HID_QUIRK_RDESC_LOGITECH 0x00000002 -#define HID_QUIRK_RDESC_SWAPPED_MIN_MAX 0x00000004 -#define HID_QUIRK_RDESC_PETALYNX 0x00000008 -#define HID_QUIRK_RDESC_MACBOOK_JIS 0x00000010 -#define HID_QUIRK_RDESC_BUTTON_CONSUMER 0x00000020 -#define HID_QUIRK_RDESC_SAMSUNG_REMOTE 0x00000040 -#define HID_QUIRK_RDESC_MICROSOFT_RECV_1028 0x00000080 -#define HID_QUIRK_RDESC_SUNPLUS_WDESKTOP 0x00000100 /* * This is the global environment of the parser. This information is @@ -411,12 +388,21 @@ struct hid_report_enum { struct hid_control_fifo { unsigned char dir; struct hid_report *report; + char *raw_report; +}; + +struct hid_output_fifo { + struct hid_report *report; + char *raw_report; }; #define HID_CLAIMED_INPUT 1 #define HID_CLAIMED_HIDDEV 2 #define HID_CLAIMED_HIDRAW 4 +#define HID_STAT_ADDED 1 +#define HID_STAT_PARSED 2 + #define HID_CTRL_RUNNING 1 #define HID_OUT_RUNNING 2 #define HID_IN_RUNNING 3 @@ -431,22 +417,28 @@ struct hid_input { struct input_dev *input; }; +struct hid_driver; +struct hid_ll_driver; + struct hid_device { /* device report descriptor */ - __u8 *rdesc; + __u8 *rdesc; unsigned rsize; struct hid_collection *collection; /* List of HID collections */ unsigned collection_size; /* Number of allocated hid_collections */ unsigned maxcollection; /* Number of parsed collections */ unsigned maxapplication; /* Number of applications */ - unsigned short bus; /* BUS ID */ - unsigned short vendor; /* Vendor ID */ - unsigned short product; /* Product ID */ - unsigned version; /* HID version */ + __u16 bus; /* BUS ID */ + __u32 vendor; /* Vendor ID */ + __u32 product; /* Product ID */ + __u32 version; /* HID version */ unsigned country; /* HID country */ struct hid_report_enum report_enum[HID_REPORT_TYPES]; - struct device *dev; /* device */ + struct device dev; /* device */ + struct hid_driver *driver; + struct hid_ll_driver *ll_driver; + unsigned int status; /* see STAT flags above */ unsigned claimed; /* Claimed by hidinput, hiddev? */ unsigned quirks; /* Various quirks the device can pull on us */ @@ -462,26 +454,29 @@ struct hid_device { /* device report descriptor */ void *driver_data; - __s32 delayed_value; /* For A4 Tech mice hwheel quirk */ - - /* device-specific function pointers */ - int (*hidinput_input_event) (struct input_dev *, unsigned int, unsigned int, int); - int (*hid_open) (struct hid_device *); - void (*hid_close) (struct hid_device *); + /* temporary hid_ff handling (until moved to the drivers) */ + int (*ff_init)(struct hid_device *); /* hiddev event handler */ + int (*hiddev_connect)(struct hid_device *, unsigned int); void (*hiddev_hid_event) (struct hid_device *, struct hid_field *field, struct hid_usage *, __s32); void (*hiddev_report_event) (struct hid_device *, struct hid_report *); /* handler for raw output data, used by hidraw */ int (*hid_output_raw_report) (struct hid_device *, __u8 *, size_t); -#ifdef CONFIG_USB_HIDINPUT_POWERBOOK - unsigned long apple_pressed_fn[BITS_TO_LONGS(KEY_CNT)]; - unsigned long pb_pressed_numlock[BITS_TO_LONGS(KEY_CNT)]; -#endif }; +static inline void *hid_get_drvdata(struct hid_device *hdev) +{ + return dev_get_drvdata(&hdev->dev); +} + +static inline void hid_set_drvdata(struct hid_device *hdev, void *data) +{ + dev_set_drvdata(&hdev->dev, data); +} + #define HID_GLOBAL_STACK_SIZE 4 #define HID_COLLECTION_STACK_SIZE 4 @@ -510,6 +505,107 @@ struct hid_descriptor { struct hid_class_descriptor desc[1]; } __attribute__ ((packed)); +#define HID_DEVICE(b, ven, prod) \ + .bus = (b), \ + .vendor = (ven), .product = (prod) + +#define HID_USB_DEVICE(ven, prod) HID_DEVICE(BUS_USB, ven, prod) +#define HID_BLUETOOTH_DEVICE(ven, prod) HID_DEVICE(BUS_BLUETOOTH, ven, prod) + +#define HID_REPORT_ID(rep) \ + .report_type = (rep) +#define HID_USAGE_ID(uhid, utype, ucode) \ + .usage_hid = (uhid), .usage_type = (utype), .usage_code = (ucode) +/* we don't want to catch types and codes equal to 0 */ +#define HID_TERMINATOR (HID_ANY_ID - 1) + +struct hid_report_id { + __u32 report_type; +}; +struct hid_usage_id { + __u32 usage_hid; + __u32 usage_type; + __u32 usage_code; +}; + +/** + * struct hid_driver + * @name: driver name (e.g. "Footech_bar-wheel") + * @id_table: which devices is this driver for (must be non-NULL for probe + * to be called) + * @probe: new device inserted + * @remove: device removed (NULL if not a hot-plug capable driver) + * @report_table: on which reports to call raw_event (NULL means all) + * @raw_event: if report in report_table, this hook is called (NULL means nop) + * @usage_table: on which events to call event (NULL means all) + * @event: if usage in usage_table, this hook is called (NULL means nop) + * @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 + * + * raw_event and event should return 0 on no action performed, 1 when no + * further processing should be done and negative on error + * + * input_mapping shall return a negative value to completely ignore this usage + * (e.g. doubled or invalid usage), zero to continue with parsing of this + * usage by generic code (no special handling needed) or positive to skip + * generic parsing (needed special handling which was done in the hook already) + * input_mapped shall return negative to inform the layer that this usage + * should not be considered for further processing or zero to notify that + * no processing was performed and should be done in a generic manner + * Both these functions may be NULL which means the same behavior as returning + * zero from them. + */ +struct hid_driver { + char *name; + const struct hid_device_id *id_table; + + int (*probe)(struct hid_device *dev, const struct hid_device_id *id); + void (*remove)(struct hid_device *dev); + + const struct hid_report_id *report_table; + int (*raw_event)(struct hid_device *hdev, struct hid_report *report, + u8 *data, int size); + const struct hid_usage_id *usage_table; + int (*event)(struct hid_device *hdev, struct hid_field *field, + struct hid_usage *usage, __s32 value); + + void (*report_fixup)(struct hid_device *hdev, __u8 *buf, + unsigned int size); + + int (*input_mapping)(struct hid_device *hdev, + struct hid_input *hidinput, struct hid_field *field, + struct hid_usage *usage, unsigned long **bit, int *max); + int (*input_mapped)(struct hid_device *hdev, + struct hid_input *hidinput, struct hid_field *field, + struct hid_usage *usage, unsigned long **bit, int *max); +/* private: */ + struct device_driver driver; +}; + +/** + * hid_ll_driver - low level driver callbacks + * @start: called on probe to start the device + * @stop: called on remove + * @open: called by input layer on open + * @close: called by input layer on close + * @hidinput_input_event: event input event (e.g. ff or leds) + * @parse: this method is called only once to parse the device data, + * shouldn't allocate anything to not leak memory + */ +struct hid_ll_driver { + int (*start)(struct hid_device *hdev); + void (*stop)(struct hid_device *hdev); + + int (*open)(struct hid_device *hdev); + void (*close)(struct hid_device *hdev); + + int (*hidinput_input_event) (struct input_dev *idev, unsigned int type, + unsigned int code, int value); + + int (*parse)(struct hid_device *hdev); +}; + /* Applications from HID Usage Tables 4/8/99 Version 1.1 */ /* We ignore a few input applications that are not widely used */ #define IS_INPUT_APPLICATION(a) (((a >= 0x00010000) && (a <= 0x00010008)) || (a == 0x00010080) || (a == 0x000c0001) || (a == 0x000d0002)) @@ -520,43 +616,157 @@ struct hid_descriptor { extern int hid_debug; #endif +extern int hid_add_device(struct hid_device *); +extern void hid_destroy_device(struct hid_device *); + +extern int __must_check __hid_register_driver(struct hid_driver *, + struct module *, const char *mod_name); +static inline int __must_check hid_register_driver(struct hid_driver *driver) +{ + return __hid_register_driver(driver, THIS_MODULE, KBUILD_MODNAME); +} +extern void hid_unregister_driver(struct hid_driver *); + extern void hidinput_hid_event(struct hid_device *, struct hid_field *, struct hid_usage *, __s32); extern void hidinput_report_event(struct hid_device *hid, struct hid_report *report); -extern int hidinput_connect(struct hid_device *); +extern int hidinput_connect(struct hid_device *hid, unsigned int force); extern void hidinput_disconnect(struct hid_device *); int hid_set_field(struct hid_field *, unsigned, __s32); int hid_input_report(struct hid_device *, int type, u8 *, int, int); int hidinput_find_field(struct hid_device *hid, unsigned int type, unsigned int code, struct hid_field **field); -int hidinput_mapping_quirks(struct hid_usage *, struct input_dev *, unsigned long **, int *); -int hidinput_event_quirks(struct hid_device *, struct hid_field *, struct hid_usage *, __s32); -int hidinput_apple_event(struct hid_device *, struct input_dev *, struct hid_usage *, __s32); void hid_output_report(struct hid_report *report, __u8 *data); -void hid_free_device(struct hid_device *device); -struct hid_device *hid_parse_report(__u8 *start, unsigned size); +struct hid_device *hid_allocate_device(void); +int hid_parse_report(struct hid_device *hid, __u8 *start, unsigned size); +int hid_connect(struct hid_device *hid, unsigned int connect_mask); + +/** + * hid_map_usage - map usage input bits + * + * @hidinput: hidinput which we are interested in + * @usage: usage to fill in + * @bit: pointer to input->{}bit (out parameter) + * @max: maximal valid usage->code to consider later (out parameter) + * @type: input event type (EV_KEY, EV_REL, ...) + * @c: code which corresponds to this usage and type + */ +static inline void hid_map_usage(struct hid_input *hidinput, + struct hid_usage *usage, unsigned long **bit, int *max, + __u8 type, __u16 c) +{ + struct input_dev *input = hidinput->input; + + usage->type = type; + usage->code = c; + + switch (type) { + case EV_ABS: + *bit = input->absbit; + *max = ABS_MAX; + break; + case EV_REL: + *bit = input->relbit; + *max = REL_MAX; + break; + case EV_KEY: + *bit = input->keybit; + *max = KEY_MAX; + break; + case EV_LED: + *bit = input->ledbit; + *max = LED_MAX; + break; + } +} + +/** + * hid_map_usage_clear - map usage input bits and clear the input bit + * + * The same as hid_map_usage, except the @c bit is also cleared in supported + * bits (@bit). + */ +static inline void hid_map_usage_clear(struct hid_input *hidinput, + struct hid_usage *usage, unsigned long **bit, int *max, + __u8 type, __u16 c) +{ + hid_map_usage(hidinput, usage, bit, max, type, c); + clear_bit(c, *bit); +} + +/** + * hid_parse - parse HW reports + * + * @hdev: hid device + * + * Call this from probe after you set up the device (if needed). Your + * report_fixup will be called (if non-NULL) after reading raw report from + * device before passing it to hid layer for real parsing. + */ +static inline int __must_check hid_parse(struct hid_device *hdev) +{ + int ret; + + if (hdev->status & HID_STAT_PARSED) + return 0; + + ret = hdev->ll_driver->parse(hdev); + if (!ret) + hdev->status |= HID_STAT_PARSED; + + return ret; +} + +/** + * hid_hw_start - start underlaying HW + * + * @hdev: hid device + * @connect_mask: which outputs to connect, see HID_CONNECT_* + * + * Call this in probe function *after* hid_parse. This will setup HW buffers + * and start the device (if not deffered to device open). hid_hw_stop must be + * called if this was successfull. + */ +static inline int __must_check hid_hw_start(struct hid_device *hdev, + unsigned int connect_mask) +{ + int ret = hdev->ll_driver->start(hdev); + if (ret || !connect_mask) + return ret; + ret = hid_connect(hdev, connect_mask); + if (ret) + hdev->ll_driver->stop(hdev); + return ret; +} + +/** + * hid_hw_stop - stop underlaying HW + * + * @hdev: hid device + * + * This is usually called from remove function or from probe when something + * failed and hid_hw_start was called already. + */ +static inline void hid_hw_stop(struct hid_device *hdev) +{ + hdev->ll_driver->stop(hdev); +} + +void hid_report_raw_event(struct hid_device *hid, int type, u8 *data, int size, + int interrupt); + +extern int hid_generic_init(void); +extern void hid_generic_exit(void); /* HID quirks API */ u32 usbhid_lookup_quirk(const u16 idVendor, const u16 idProduct); int usbhid_quirks_init(char **quirks_param); void usbhid_quirks_exit(void); -void usbhid_fixup_report_descriptor(const u16, const u16, char *, unsigned, char **); - -#ifdef CONFIG_HID_FF -int hid_ff_init(struct hid_device *hid); +void usbhid_set_leds(struct hid_device *hid); -int hid_lgff_init(struct hid_device *hid); -int hid_lg2ff_init(struct hid_device *hid); -int hid_plff_init(struct hid_device *hid); -int hid_tmff_init(struct hid_device *hid); -int hid_zpff_init(struct hid_device *hid); #ifdef CONFIG_HID_PID int hid_pidff_init(struct hid_device *hid); #else -static inline int hid_pidff_init(struct hid_device *hid) { return -ENODEV; } -#endif - -#else -static inline int hid_ff_init(struct hid_device *hid) { return -1; } +#define hid_pidff_init NULL #endif #ifdef CONFIG_HID_DEBUG @@ -572,10 +782,23 @@ dbg_hid(const char *fmt, ...) return 0; } #define dbg_hid_line dbg_hid -#endif +#endif /* HID_DEBUG */ #define err_hid(format, arg...) printk(KERN_ERR "%s: " format "\n" , \ __FILE__ , ## arg) -#endif +#endif /* HID_FF */ + +#ifdef CONFIG_HID_COMPAT +#define HID_COMPAT_LOAD_DRIVER(name) \ +void hid_compat_##name(void) { } \ +EXPORT_SYMBOL(hid_compat_##name) +#else +#define HID_COMPAT_LOAD_DRIVER(name) +#endif /* HID_COMPAT */ +#define HID_COMPAT_CALL_DRIVER(name) do { \ + extern void hid_compat_##name(void); \ + hid_compat_##name(); \ +} while (0) + #endif diff --git a/include/linux/hiddev.h b/include/linux/hiddev.h index a416b904ba90..c760ae0eb6a1 100644 --- a/include/linux/hiddev.h +++ b/include/linux/hiddev.h @@ -182,26 +182,28 @@ struct hiddev_usage_ref_multi { /* To traverse the input report descriptor info for a HID device, perform the * following: * - * rinfo.report_type = HID_REPORT_TYPE_INPUT; - * rinfo.report_id = HID_REPORT_ID_FIRST; - * ret = ioctl(fd, HIDIOCGREPORTINFO, &rinfo); + * rinfo.report_type = HID_REPORT_TYPE_INPUT; + * rinfo.report_id = HID_REPORT_ID_FIRST; + * ret = ioctl(fd, HIDIOCGREPORTINFO, &rinfo); * - * while (ret >= 0) { - * for (i = 0; i < rinfo.num_fields; i++) { - * finfo.report_type = rinfo.report_type; - * finfo.report_id = rinfo.report_id; - * finfo.field_index = i; - * ioctl(fd, HIDIOCGFIELDINFO, &finfo); - * for (j = 0; j < finfo.maxusage; j++) { - * uref.field_index = i; - * uref.usage_index = j; - * ioctl(fd, HIDIOCGUCODE, &uref); - * ioctl(fd, HIDIOCGUSAGE, &uref); - * } - * } - * rinfo.report_id |= HID_REPORT_ID_NEXT; - * ret = ioctl(fd, HIDIOCGREPORTINFO, &rinfo); - * } + * while (ret >= 0) { + * for (i = 0; i < rinfo.num_fields; i++) { + * finfo.report_type = rinfo.report_type; + * finfo.report_id = rinfo.report_id; + * finfo.field_index = i; + * ioctl(fd, HIDIOCGFIELDINFO, &finfo); + * for (j = 0; j < finfo.maxusage; j++) { + * uref.report_type = rinfo.report_type; + * uref.report_id = rinfo.report_id; + * uref.field_index = i; + * uref.usage_index = j; + * ioctl(fd, HIDIOCGUCODE, &uref); + * ioctl(fd, HIDIOCGUSAGE, &uref); + * } + * } + * rinfo.report_id |= HID_REPORT_ID_NEXT; + * ret = ioctl(fd, HIDIOCGREPORTINFO, &rinfo); + * } */ @@ -217,7 +219,7 @@ struct hid_field; struct hid_report; #ifdef CONFIG_USB_HIDDEV -int hiddev_connect(struct hid_device *); +int hiddev_connect(struct hid_device *hid, unsigned int force); void hiddev_disconnect(struct hid_device *); void hiddev_hid_event(struct hid_device *hid, struct hid_field *field, struct hid_usage *usage, __s32 value); @@ -225,7 +227,9 @@ void hiddev_report_event(struct hid_device *hid, struct hid_report *report); int __init hiddev_init(void); void hiddev_exit(void); #else -static inline int hiddev_connect(struct hid_device *hid) { return -1; } +static inline int hiddev_connect(struct hid_device *hid, + unsigned int force) +{ return -1; } static inline void hiddev_disconnect(struct hid_device *hid) { } static inline void hiddev_hid_event(struct hid_device *hid, struct hid_field *field, struct hid_usage *usage, __s32 value) { } diff --git a/include/linux/if_ether.h b/include/linux/if_ether.h index bf1a53b2682e..7f3c735f422b 100644 --- a/include/linux/if_ether.h +++ b/include/linux/if_ether.h @@ -9,7 +9,7 @@ * * Author: Fred N. van Kempen, <waltje@uWalt.NL.Mugnet.ORG> * Donald Becker, <becker@super.org> - * Alan Cox, <alan@redhat.com> + * Alan Cox, <alan@lxorguk.ukuu.org.uk> * Steve Whitehouse, <gw7rrm@eeshack3.swan.ac.uk> * * This program is free software; you can redistribute it and/or diff --git a/include/linux/if_fddi.h b/include/linux/if_fddi.h index ae77daed6c2f..45de1046dbbf 100644 --- a/include/linux/if_fddi.h +++ b/include/linux/if_fddi.h @@ -12,7 +12,7 @@ * if_fddi.h is based on previous if_ether.h and if_tr.h work by * Fred N. van Kempen, <waltje@uWalt.NL.Mugnet.ORG> * Donald Becker, <becker@super.org> - * Alan Cox, <alan@redhat.com> + * Alan Cox, <alan@lxorguk.ukuu.org.uk> * Steve Whitehouse, <gw7rrm@eeshack3.swan.ac.uk> * Peter De Schrijver, <stud11@cc4.kuleuven.ac.be> * diff --git a/include/linux/if_hippi.h b/include/linux/if_hippi.h index 94d31ca7d71a..f0f23516bb59 100644 --- a/include/linux/if_hippi.h +++ b/include/linux/if_hippi.h @@ -9,7 +9,7 @@ * * Author: Fred N. van Kempen, <waltje@uWalt.NL.Mugnet.ORG> * Donald Becker, <becker@super.org> - * Alan Cox, <alan@redhat.com> + * Alan Cox, <alan@lxorguk.ukuu.org.uk> * Steve Whitehouse, <gw7rrm@eeshack3.swan.ac.uk> * Jes Sorensen, <Jes.Sorensen@cern.ch> * diff --git a/include/linux/igmp.h b/include/linux/igmp.h index 7bb3c095c15b..f734a0ba0698 100644 --- a/include/linux/igmp.h +++ b/include/linux/igmp.h @@ -2,7 +2,7 @@ * Linux NET3: Internet Group Management Protocol [IGMP] * * Authors: - * Alan Cox <Alan.Cox@linux.org> + * Alan Cox <alan@lxorguk.ukuu.org.uk> * * Extended to talk the BSD extended IGMP protocol of mrouted 3.6 * diff --git a/include/linux/lockd/bind.h b/include/linux/lockd/bind.h index 3d25bcd139d1..e5872dc994c0 100644 --- a/include/linux/lockd/bind.h +++ b/include/linux/lockd/bind.h @@ -27,7 +27,6 @@ struct nlmsvc_binding { struct nfs_fh *, struct file **); void (*fclose)(struct file *); - unsigned long (*get_grace_period)(void); }; extern struct nlmsvc_binding * nlmsvc_ops; @@ -53,15 +52,7 @@ extern void nlmclnt_done(struct nlm_host *host); extern int nlmclnt_proc(struct nlm_host *host, int cmd, struct file_lock *fl); -extern int lockd_up(int proto); +extern int lockd_up(void); extern void lockd_down(void); -unsigned long get_nfs_grace_period(void); - -#ifdef CONFIG_NFSD_V4 -unsigned long get_nfs4_grace_period(void); -#else -static inline unsigned long get_nfs4_grace_period(void) {return 0;} -#endif - #endif /* LINUX_LOCKD_BIND_H */ diff --git a/include/linux/lockd/lockd.h b/include/linux/lockd/lockd.h index dbb87ab282e8..b56d5aa9b194 100644 --- a/include/linux/lockd/lockd.h +++ b/include/linux/lockd/lockd.h @@ -12,6 +12,8 @@ #ifdef __KERNEL__ #include <linux/in.h> +#include <linux/in6.h> +#include <net/ipv6.h> #include <linux/fs.h> #include <linux/kref.h> #include <linux/utsname.h> @@ -38,8 +40,9 @@ */ struct nlm_host { struct hlist_node h_hash; /* doubly linked list */ - struct sockaddr_in h_addr; /* peer address */ - struct sockaddr_in h_saddr; /* our address (optional) */ + struct sockaddr_storage h_addr; /* peer address */ + size_t h_addrlen; + struct sockaddr_storage h_srcaddr; /* our address (optional) */ struct rpc_clnt * h_rpcclnt; /* RPC client to talk to peer */ char * h_name; /* remote hostname */ u32 h_version; /* interface version */ @@ -61,18 +64,56 @@ struct nlm_host { struct list_head h_granted; /* Locks in GRANTED state */ struct list_head h_reclaim; /* Locks in RECLAIM state */ struct nsm_handle * h_nsmhandle; /* NSM status handle */ + + char h_addrbuf[48], /* address eyecatchers */ + h_srcaddrbuf[48]; }; struct nsm_handle { struct list_head sm_link; atomic_t sm_count; char * sm_name; - struct sockaddr_in sm_addr; + struct sockaddr_storage sm_addr; + size_t sm_addrlen; unsigned int sm_monitored : 1, sm_sticky : 1; /* don't unmonitor */ + char sm_addrbuf[48]; /* address eyecatcher */ }; /* + * Rigorous type checking on sockaddr type conversions + */ +static inline struct sockaddr_in *nlm_addr_in(const struct nlm_host *host) +{ + return (struct sockaddr_in *)&host->h_addr; +} + +static inline struct sockaddr *nlm_addr(const struct nlm_host *host) +{ + return (struct sockaddr *)&host->h_addr; +} + +static inline struct sockaddr_in *nlm_srcaddr_in(const struct nlm_host *host) +{ + return (struct sockaddr_in *)&host->h_srcaddr; +} + +static inline struct sockaddr *nlm_srcaddr(const struct nlm_host *host) +{ + return (struct sockaddr *)&host->h_srcaddr; +} + +static inline struct sockaddr_in *nsm_addr_in(const struct nsm_handle *handle) +{ + return (struct sockaddr_in *)&handle->sm_addr; +} + +static inline struct sockaddr *nsm_addr(const struct nsm_handle *handle) +{ + return (struct sockaddr *)&handle->sm_addr; +} + +/* * Map an fl_owner_t into a unique 32-bit "pid" */ struct nlm_lockowner { @@ -166,7 +207,8 @@ int nlm_async_reply(struct nlm_rqst *, u32, const struct rpc_call_ops *); struct nlm_wait * nlmclnt_prepare_block(struct nlm_host *host, struct file_lock *fl); void nlmclnt_finish_block(struct nlm_wait *block); int nlmclnt_block(struct nlm_wait *block, struct nlm_rqst *req, long timeout); -__be32 nlmclnt_grant(const struct sockaddr_in *addr, const struct nlm_lock *); +__be32 nlmclnt_grant(const struct sockaddr *addr, + const struct nlm_lock *lock); void nlmclnt_recovery(struct nlm_host *); int nlmclnt_reclaim(struct nlm_host *, struct file_lock *); void nlmclnt_next_cookie(struct nlm_cookie *); @@ -174,12 +216,14 @@ void nlmclnt_next_cookie(struct nlm_cookie *); /* * Host cache */ -struct nlm_host *nlmclnt_lookup_host(const struct sockaddr_in *sin, - int proto, u32 version, +struct nlm_host *nlmclnt_lookup_host(const struct sockaddr *sap, + const size_t salen, + const unsigned short protocol, + const u32 version, + const char *hostname); +struct nlm_host *nlmsvc_lookup_host(const struct svc_rqst *rqstp, const char *hostname, - unsigned int hostname_len); -struct nlm_host *nlmsvc_lookup_host(struct svc_rqst *, const char *, - unsigned int); + const size_t hostname_len); struct rpc_clnt * nlm_bind_host(struct nlm_host *); void nlm_rebind_host(struct nlm_host *); struct nlm_host * nlm_get_host(struct nlm_host *); @@ -201,7 +245,7 @@ typedef int (*nlm_host_match_fn_t)(void *cur, struct nlm_host *ref); */ __be32 nlmsvc_lock(struct svc_rqst *, struct nlm_file *, struct nlm_host *, struct nlm_lock *, int, - struct nlm_cookie *); + struct nlm_cookie *, int); __be32 nlmsvc_unlock(struct nlm_file *, struct nlm_lock *); __be32 nlmsvc_testlock(struct svc_rqst *, struct nlm_file *, struct nlm_host *, struct nlm_lock *, @@ -233,15 +277,82 @@ static inline struct inode *nlmsvc_file_inode(struct nlm_file *file) return file->f_file->f_path.dentry->d_inode; } +static inline int __nlm_privileged_request4(const struct sockaddr *sap) +{ + const struct sockaddr_in *sin = (struct sockaddr_in *)sap; + return (sin->sin_addr.s_addr == htonl(INADDR_LOOPBACK)) && + (ntohs(sin->sin_port) < 1024); +} + +#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE) +static inline int __nlm_privileged_request6(const struct sockaddr *sap) +{ + const struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *)sap; + return (ipv6_addr_type(&sin6->sin6_addr) & IPV6_ADDR_LOOPBACK) && + (ntohs(sin6->sin6_port) < 1024); +} +#else /* defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE) */ +static inline int __nlm_privileged_request6(const struct sockaddr *sap) +{ + return 0; +} +#endif /* defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE) */ + /* - * Compare two host addresses (needs modifying for ipv6) + * Ensure incoming requests are from local privileged callers. + * + * Return TRUE if sender is local and is connecting via a privileged port; + * otherwise return FALSE. */ -static inline int nlm_cmp_addr(const struct sockaddr_in *sin1, - const struct sockaddr_in *sin2) +static inline int nlm_privileged_requester(const struct svc_rqst *rqstp) { + const struct sockaddr *sap = svc_addr(rqstp); + + switch (sap->sa_family) { + case AF_INET: + return __nlm_privileged_request4(sap); + case AF_INET6: + return __nlm_privileged_request6(sap); + default: + return 0; + } +} + +static inline int __nlm_cmp_addr4(const struct sockaddr *sap1, + const struct sockaddr *sap2) +{ + const struct sockaddr_in *sin1 = (const struct sockaddr_in *)sap1; + const struct sockaddr_in *sin2 = (const struct sockaddr_in *)sap2; return sin1->sin_addr.s_addr == sin2->sin_addr.s_addr; } +static inline int __nlm_cmp_addr6(const struct sockaddr *sap1, + const struct sockaddr *sap2) +{ + const struct sockaddr_in6 *sin1 = (const struct sockaddr_in6 *)sap1; + const struct sockaddr_in6 *sin2 = (const struct sockaddr_in6 *)sap2; + return ipv6_addr_equal(&sin1->sin6_addr, &sin2->sin6_addr); +} + +/* + * Compare two host addresses + * + * Return TRUE if the addresses are the same; otherwise FALSE. + */ +static inline int nlm_cmp_addr(const struct sockaddr *sap1, + const struct sockaddr *sap2) +{ + if (sap1->sa_family == sap2->sa_family) { + switch (sap1->sa_family) { + case AF_INET: + return __nlm_cmp_addr4(sap1, sap2); + case AF_INET6: + return __nlm_cmp_addr6(sap1, sap2); + } + } + return 0; +} + /* * Compare two NLM locks. * When the second lock is of type F_UNLCK, this acts like a wildcard. diff --git a/include/linux/lockd/xdr.h b/include/linux/lockd/xdr.h index df18fa053bcd..d6b3a802c046 100644 --- a/include/linux/lockd/xdr.h +++ b/include/linux/lockd/xdr.h @@ -81,8 +81,6 @@ struct nlm_reboot { unsigned int len; u32 state; __be32 addr; - __be32 vers; - __be32 proto; }; /* diff --git a/include/linux/mfd/wm8350/audio.h b/include/linux/mfd/wm8350/audio.h new file mode 100644 index 000000000000..217bb22ebb8e --- /dev/null +++ b/include/linux/mfd/wm8350/audio.h @@ -0,0 +1,598 @@ +/* + * audio.h -- Audio Driver for Wolfson WM8350 PMIC + * + * Copyright 2007 Wolfson Microelectronics PLC + * + * 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 __LINUX_MFD_WM8350_AUDIO_H_ +#define __LINUX_MFD_WM8350_AUDIO_H_ + +#include <linux/platform_device.h> + +#define WM8350_CLOCK_CONTROL_1 0x28 +#define WM8350_CLOCK_CONTROL_2 0x29 +#define WM8350_FLL_CONTROL_1 0x2A +#define WM8350_FLL_CONTROL_2 0x2B +#define WM8350_FLL_CONTROL_3 0x2C +#define WM8350_FLL_CONTROL_4 0x2D +#define WM8350_DAC_CONTROL 0x30 +#define WM8350_DAC_DIGITAL_VOLUME_L 0x32 +#define WM8350_DAC_DIGITAL_VOLUME_R 0x33 +#define WM8350_DAC_LR_RATE 0x35 +#define WM8350_DAC_CLOCK_CONTROL 0x36 +#define WM8350_DAC_MUTE 0x3A +#define WM8350_DAC_MUTE_VOLUME 0x3B +#define WM8350_DAC_SIDE 0x3C +#define WM8350_ADC_CONTROL 0x40 +#define WM8350_ADC_DIGITAL_VOLUME_L 0x42 +#define WM8350_ADC_DIGITAL_VOLUME_R 0x43 +#define WM8350_ADC_DIVIDER 0x44 +#define WM8350_ADC_LR_RATE 0x46 +#define WM8350_INPUT_CONTROL 0x48 +#define WM8350_IN3_INPUT_CONTROL 0x49 +#define WM8350_MIC_BIAS_CONTROL 0x4A +#define WM8350_OUTPUT_CONTROL 0x4C +#define WM8350_JACK_DETECT 0x4D +#define WM8350_ANTI_POP_CONTROL 0x4E +#define WM8350_LEFT_INPUT_VOLUME 0x50 +#define WM8350_RIGHT_INPUT_VOLUME 0x51 +#define WM8350_LEFT_MIXER_CONTROL 0x58 +#define WM8350_RIGHT_MIXER_CONTROL 0x59 +#define WM8350_OUT3_MIXER_CONTROL 0x5C +#define WM8350_OUT4_MIXER_CONTROL 0x5D +#define WM8350_OUTPUT_LEFT_MIXER_VOLUME 0x60 +#define WM8350_OUTPUT_RIGHT_MIXER_VOLUME 0x61 +#define WM8350_INPUT_MIXER_VOLUME_L 0x62 +#define WM8350_INPUT_MIXER_VOLUME_R 0x63 +#define WM8350_INPUT_MIXER_VOLUME 0x64 +#define WM8350_LOUT1_VOLUME 0x68 +#define WM8350_ROUT1_VOLUME 0x69 +#define WM8350_LOUT2_VOLUME 0x6A +#define WM8350_ROUT2_VOLUME 0x6B +#define WM8350_BEEP_VOLUME 0x6F +#define WM8350_AI_FORMATING 0x70 +#define WM8350_ADC_DAC_COMP 0x71 +#define WM8350_AI_ADC_CONTROL 0x72 +#define WM8350_AI_DAC_CONTROL 0x73 +#define WM8350_AIF_TEST 0x74 +#define WM8350_JACK_PIN_STATUS 0xE7 + +/* Bit values for R08 (0x08) */ +#define WM8350_CODEC_ISEL_1_5 0 /* x1.5 */ +#define WM8350_CODEC_ISEL_1_0 1 /* x1.0 */ +#define WM8350_CODEC_ISEL_0_75 2 /* x0.75 */ +#define WM8350_CODEC_ISEL_0_5 3 /* x0.5 */ + +#define WM8350_VMID_OFF 0 +#define WM8350_VMID_500K 1 +#define WM8350_VMID_100K 2 +#define WM8350_VMID_10K 3 + +/* + * R40 (0x28) - Clock Control 1 + */ +#define WM8350_TOCLK_RATE 0x4000 +#define WM8350_MCLK_SEL 0x0800 +#define WM8350_MCLK_DIV_MASK 0x0100 +#define WM8350_BCLK_DIV_MASK 0x00F0 +#define WM8350_OPCLK_DIV_MASK 0x0007 + +/* + * R41 (0x29) - Clock Control 2 + */ +#define WM8350_LRC_ADC_SEL 0x8000 +#define WM8350_MCLK_DIR 0x0001 + +/* + * R42 (0x2A) - FLL Control 1 + */ +#define WM8350_FLL_DITHER_WIDTH_MASK 0x3000 +#define WM8350_FLL_DITHER_HP 0x0800 +#define WM8350_FLL_OUTDIV_MASK 0x0700 +#define WM8350_FLL_RSP_RATE_MASK 0x00F0 +#define WM8350_FLL_RATE_MASK 0x0007 + +/* + * R43 (0x2B) - FLL Control 2 + */ +#define WM8350_FLL_RATIO_MASK 0xF800 +#define WM8350_FLL_N_MASK 0x03FF + +/* + * R44 (0x2C) - FLL Control 3 + */ +#define WM8350_FLL_K_MASK 0xFFFF + +/* + * R45 (0x2D) - FLL Control 4 + */ +#define WM8350_FLL_FRAC 0x0020 +#define WM8350_FLL_SLOW_LOCK_REF 0x0010 +#define WM8350_FLL_CLK_SRC_MASK 0x0003 + +/* + * R48 (0x30) - DAC Control + */ +#define WM8350_DAC_MONO 0x2000 +#define WM8350_AIF_LRCLKRATE 0x1000 +#define WM8350_DEEMP_MASK 0x0030 +#define WM8350_DACL_DATINV 0x0002 +#define WM8350_DACR_DATINV 0x0001 + +/* + * R50 (0x32) - DAC Digital Volume L + */ +#define WM8350_DAC_VU 0x0100 +#define WM8350_DACL_VOL_MASK 0x00FF + +/* + * R51 (0x33) - DAC Digital Volume R + */ +#define WM8350_DAC_VU 0x0100 +#define WM8350_DACR_VOL_MASK 0x00FF + +/* + * R53 (0x35) - DAC LR Rate + */ +#define WM8350_DACLRC_ENA 0x0800 +#define WM8350_DACLRC_RATE_MASK 0x07FF + +/* + * R54 (0x36) - DAC Clock Control + */ +#define WM8350_DACCLK_POL 0x0010 +#define WM8350_DAC_CLKDIV_MASK 0x0007 + +/* + * R58 (0x3A) - DAC Mute + */ +#define WM8350_DAC_MUTE_ENA 0x4000 + +/* + * R59 (0x3B) - DAC Mute Volume + */ +#define WM8350_DAC_MUTEMODE 0x4000 +#define WM8350_DAC_MUTERATE 0x2000 +#define WM8350_DAC_SB_FILT 0x1000 + +/* + * R60 (0x3C) - DAC Side + */ +#define WM8350_ADC_TO_DACL_MASK 0x3000 +#define WM8350_ADC_TO_DACR_MASK 0x0C00 + +/* + * R64 (0x40) - ADC Control + */ +#define WM8350_ADC_HPF_CUT_MASK 0x0300 +#define WM8350_ADCL_DATINV 0x0002 +#define WM8350_ADCR_DATINV 0x0001 + +/* + * R66 (0x42) - ADC Digital Volume L + */ +#define WM8350_ADC_VU 0x0100 +#define WM8350_ADCL_VOL_MASK 0x00FF + +/* + * R67 (0x43) - ADC Digital Volume R + */ +#define WM8350_ADC_VU 0x0100 +#define WM8350_ADCR_VOL_MASK 0x00FF + +/* + * R68 (0x44) - ADC Divider + */ +#define WM8350_ADCL_DAC_SVOL_MASK 0x0F00 +#define WM8350_ADCR_DAC_SVOL_MASK 0x00F0 +#define WM8350_ADCCLK_POL 0x0008 +#define WM8350_ADC_CLKDIV_MASK 0x0007 + +/* + * R70 (0x46) - ADC LR Rate + */ +#define WM8350_ADCLRC_ENA 0x0800 +#define WM8350_ADCLRC_RATE_MASK 0x07FF + +/* + * R72 (0x48) - Input Control + */ +#define WM8350_IN2R_ENA 0x0400 +#define WM8350_IN1RN_ENA 0x0200 +#define WM8350_IN1RP_ENA 0x0100 +#define WM8350_IN2L_ENA 0x0004 +#define WM8350_IN1LN_ENA 0x0002 +#define WM8350_IN1LP_ENA 0x0001 + +/* + * R73 (0x49) - IN3 Input Control + */ +#define WM8350_IN3R_SHORT 0x4000 +#define WM8350_IN3L_SHORT 0x0040 + +/* + * R74 (0x4A) - Mic Bias Control + */ +#define WM8350_MICBSEL 0x4000 +#define WM8350_MCDTHR_MASK 0x001C +#define WM8350_MCDSCTHR_MASK 0x0003 + +/* + * R76 (0x4C) - Output Control + */ +#define WM8350_OUT4_VROI 0x0800 +#define WM8350_OUT3_VROI 0x0400 +#define WM8350_OUT2_VROI 0x0200 +#define WM8350_OUT1_VROI 0x0100 +#define WM8350_OUT2_FB 0x0004 +#define WM8350_OUT1_FB 0x0001 + +/* + * R77 (0x4D) - Jack Detect + */ +#define WM8350_JDL_ENA 0x8000 +#define WM8350_JDR_ENA 0x4000 + +/* + * R78 (0x4E) - Anti Pop Control + */ +#define WM8350_ANTI_POP_MASK 0x0300 +#define WM8350_DIS_OP_LN4_MASK 0x00C0 +#define WM8350_DIS_OP_LN3_MASK 0x0030 +#define WM8350_DIS_OP_OUT2_MASK 0x000C +#define WM8350_DIS_OP_OUT1_MASK 0x0003 + +/* + * R80 (0x50) - Left Input Volume + */ +#define WM8350_INL_MUTE 0x4000 +#define WM8350_INL_ZC 0x2000 +#define WM8350_IN_VU 0x0100 +#define WM8350_INL_VOL_MASK 0x00FC + +/* + * R81 (0x51) - Right Input Volume + */ +#define WM8350_INR_MUTE 0x4000 +#define WM8350_INR_ZC 0x2000 +#define WM8350_IN_VU 0x0100 +#define WM8350_INR_VOL_MASK 0x00FC + +/* + * R88 (0x58) - Left Mixer Control + */ +#define WM8350_DACR_TO_MIXOUTL 0x1000 +#define WM8350_DACL_TO_MIXOUTL 0x0800 +#define WM8350_IN3L_TO_MIXOUTL 0x0004 +#define WM8350_INR_TO_MIXOUTL 0x0002 +#define WM8350_INL_TO_MIXOUTL 0x0001 + +/* + * R89 (0x59) - Right Mixer Control + */ +#define WM8350_DACR_TO_MIXOUTR 0x1000 +#define WM8350_DACL_TO_MIXOUTR 0x0800 +#define WM8350_IN3R_TO_MIXOUTR 0x0008 +#define WM8350_INR_TO_MIXOUTR 0x0002 +#define WM8350_INL_TO_MIXOUTR 0x0001 + +/* + * R92 (0x5C) - OUT3 Mixer Control + */ +#define WM8350_DACL_TO_OUT3 0x0800 +#define WM8350_MIXINL_TO_OUT3 0x0100 +#define WM8350_OUT4_TO_OUT3 0x0008 +#define WM8350_MIXOUTL_TO_OUT3 0x0001 + +/* + * R93 (0x5D) - OUT4 Mixer Control + */ +#define WM8350_DACR_TO_OUT4 0x1000 +#define WM8350_DACL_TO_OUT4 0x0800 +#define WM8350_OUT4_ATTN 0x0400 +#define WM8350_MIXINR_TO_OUT4 0x0200 +#define WM8350_OUT3_TO_OUT4 0x0004 +#define WM8350_MIXOUTR_TO_OUT4 0x0002 +#define WM8350_MIXOUTL_TO_OUT4 0x0001 + +/* + * R96 (0x60) - Output Left Mixer Volume + */ +#define WM8350_IN3L_MIXOUTL_VOL_MASK 0x0E00 +#define WM8350_IN3L_MIXOUTL_VOL_SHIFT 9 +#define WM8350_INR_MIXOUTL_VOL_MASK 0x00E0 +#define WM8350_INR_MIXOUTL_VOL_SHIFT 5 +#define WM8350_INL_MIXOUTL_VOL_MASK 0x000E +#define WM8350_INL_MIXOUTL_VOL_SHIFT 1 + +/* Bit values for R96 (0x60) */ +#define WM8350_IN3L_MIXOUTL_VOL_OFF 0 +#define WM8350_IN3L_MIXOUTL_VOL_M12DB 1 +#define WM8350_IN3L_MIXOUTL_VOL_M9DB 2 +#define WM8350_IN3L_MIXOUTL_VOL_M6DB 3 +#define WM8350_IN3L_MIXOUTL_VOL_M3DB 4 +#define WM8350_IN3L_MIXOUTL_VOL_0DB 5 +#define WM8350_IN3L_MIXOUTL_VOL_3DB 6 +#define WM8350_IN3L_MIXOUTL_VOL_6DB 7 + +#define WM8350_INR_MIXOUTL_VOL_OFF 0 +#define WM8350_INR_MIXOUTL_VOL_M12DB 1 +#define WM8350_INR_MIXOUTL_VOL_M9DB 2 +#define WM8350_INR_MIXOUTL_VOL_M6DB 3 +#define WM8350_INR_MIXOUTL_VOL_M3DB 4 +#define WM8350_INR_MIXOUTL_VOL_0DB 5 +#define WM8350_INR_MIXOUTL_VOL_3DB 6 +#define WM8350_INR_MIXOUTL_VOL_6DB 7 + +#define WM8350_INL_MIXOUTL_VOL_OFF 0 +#define WM8350_INL_MIXOUTL_VOL_M12DB 1 +#define WM8350_INL_MIXOUTL_VOL_M9DB 2 +#define WM8350_INL_MIXOUTL_VOL_M6DB 3 +#define WM8350_INL_MIXOUTL_VOL_M3DB 4 +#define WM8350_INL_MIXOUTL_VOL_0DB 5 +#define WM8350_INL_MIXOUTL_VOL_3DB 6 +#define WM8350_INL_MIXOUTL_VOL_6DB 7 + +/* + * R97 (0x61) - Output Right Mixer Volume + */ +#define WM8350_IN3R_MIXOUTR_VOL_MASK 0xE000 +#define WM8350_IN3R_MIXOUTR_VOL_SHIFT 13 +#define WM8350_INR_MIXOUTR_VOL_MASK 0x00E0 +#define WM8350_INR_MIXOUTR_VOL_SHIFT 5 +#define WM8350_INL_MIXOUTR_VOL_MASK 0x000E +#define WM8350_INL_MIXOUTR_VOL_SHIFT 1 + +/* Bit values for R96 (0x60) */ +#define WM8350_IN3R_MIXOUTR_VOL_OFF 0 +#define WM8350_IN3R_MIXOUTR_VOL_M12DB 1 +#define WM8350_IN3R_MIXOUTR_VOL_M9DB 2 +#define WM8350_IN3R_MIXOUTR_VOL_M6DB 3 +#define WM8350_IN3R_MIXOUTR_VOL_M3DB 4 +#define WM8350_IN3R_MIXOUTR_VOL_0DB 5 +#define WM8350_IN3R_MIXOUTR_VOL_3DB 6 +#define WM8350_IN3R_MIXOUTR_VOL_6DB 7 + +#define WM8350_INR_MIXOUTR_VOL_OFF 0 +#define WM8350_INR_MIXOUTR_VOL_M12DB 1 +#define WM8350_INR_MIXOUTR_VOL_M9DB 2 +#define WM8350_INR_MIXOUTR_VOL_M6DB 3 +#define WM8350_INR_MIXOUTR_VOL_M3DB 4 +#define WM8350_INR_MIXOUTR_VOL_0DB 5 +#define WM8350_INR_MIXOUTR_VOL_3DB 6 +#define WM8350_INR_MIXOUTR_VOL_6DB 7 + +#define WM8350_INL_MIXOUTR_VOL_OFF 0 +#define WM8350_INL_MIXOUTR_VOL_M12DB 1 +#define WM8350_INL_MIXOUTR_VOL_M9DB 2 +#define WM8350_INL_MIXOUTR_VOL_M6DB 3 +#define WM8350_INL_MIXOUTR_VOL_M3DB 4 +#define WM8350_INL_MIXOUTR_VOL_0DB 5 +#define WM8350_INL_MIXOUTR_VOL_3DB 6 +#define WM8350_INL_MIXOUTR_VOL_6DB 7 + +/* + * R98 (0x62) - Input Mixer Volume L + */ +#define WM8350_IN3L_MIXINL_VOL_MASK 0x0E00 +#define WM8350_IN2L_MIXINL_VOL_MASK 0x000E +#define WM8350_INL_MIXINL_VOL 0x0001 + +/* + * R99 (0x63) - Input Mixer Volume R + */ +#define WM8350_IN3R_MIXINR_VOL_MASK 0xE000 +#define WM8350_IN2R_MIXINR_VOL_MASK 0x00E0 +#define WM8350_INR_MIXINR_VOL 0x0001 + +/* + * R100 (0x64) - Input Mixer Volume + */ +#define WM8350_OUT4_MIXIN_DST 0x8000 +#define WM8350_OUT4_MIXIN_VOL_MASK 0x000E + +/* + * R104 (0x68) - LOUT1 Volume + */ +#define WM8350_OUT1L_MUTE 0x4000 +#define WM8350_OUT1L_ZC 0x2000 +#define WM8350_OUT1_VU 0x0100 +#define WM8350_OUT1L_VOL_MASK 0x00FC +#define WM8350_OUT1L_VOL_SHIFT 2 + +/* + * R105 (0x69) - ROUT1 Volume + */ +#define WM8350_OUT1R_MUTE 0x4000 +#define WM8350_OUT1R_ZC 0x2000 +#define WM8350_OUT1_VU 0x0100 +#define WM8350_OUT1R_VOL_MASK 0x00FC +#define WM8350_OUT1R_VOL_SHIFT 2 + +/* + * R106 (0x6A) - LOUT2 Volume + */ +#define WM8350_OUT2L_MUTE 0x4000 +#define WM8350_OUT2L_ZC 0x2000 +#define WM8350_OUT2_VU 0x0100 +#define WM8350_OUT2L_VOL_MASK 0x00FC + +/* + * R107 (0x6B) - ROUT2 Volume + */ +#define WM8350_OUT2R_MUTE 0x4000 +#define WM8350_OUT2R_ZC 0x2000 +#define WM8350_OUT2R_INV 0x0400 +#define WM8350_OUT2R_INV_MUTE 0x0200 +#define WM8350_OUT2_VU 0x0100 +#define WM8350_OUT2R_VOL_MASK 0x00FC + +/* + * R111 (0x6F) - BEEP Volume + */ +#define WM8350_IN3R_OUT2R_VOL_MASK 0x00E0 + +/* + * R112 (0x70) - AI Formating + */ +#define WM8350_AIF_BCLK_INV 0x8000 +#define WM8350_AIF_TRI 0x2000 +#define WM8350_AIF_LRCLK_INV 0x1000 +#define WM8350_AIF_WL_MASK 0x0C00 +#define WM8350_AIF_FMT_MASK 0x0300 + +/* + * R113 (0x71) - ADC DAC COMP + */ +#define WM8350_DAC_COMP 0x0080 +#define WM8350_DAC_COMPMODE 0x0040 +#define WM8350_ADC_COMP 0x0020 +#define WM8350_ADC_COMPMODE 0x0010 +#define WM8350_LOOPBACK 0x0001 + +/* + * R114 (0x72) - AI ADC Control + */ +#define WM8350_AIFADC_PD 0x0080 +#define WM8350_AIFADCL_SRC 0x0040 +#define WM8350_AIFADCR_SRC 0x0020 +#define WM8350_AIFADC_TDM_CHAN 0x0010 +#define WM8350_AIFADC_TDM 0x0008 + +/* + * R115 (0x73) - AI DAC Control + */ +#define WM8350_BCLK_MSTR 0x4000 +#define WM8350_AIFDAC_PD 0x0080 +#define WM8350_DACL_SRC 0x0040 +#define WM8350_DACR_SRC 0x0020 +#define WM8350_AIFDAC_TDM_CHAN 0x0010 +#define WM8350_AIFDAC_TDM 0x0008 +#define WM8350_DAC_BOOST_MASK 0x0003 + +/* + * R116 (0x74) - AIF Test + */ +#define WM8350_CODEC_BYP 0x4000 +#define WM8350_AIFADC_WR_TST 0x2000 +#define WM8350_AIFADC_RD_TST 0x1000 +#define WM8350_AIFDAC_WR_TST 0x0800 +#define WM8350_AIFDAC_RD_TST 0x0400 +#define WM8350_AIFADC_ASYN 0x0020 +#define WM8350_AIFDAC_ASYN 0x0010 + +/* + * R231 (0xE7) - Jack Status + */ +#define WM8350_JACK_R_LVL 0x0400 + +/* + * WM8350 Platform setup + */ +#define WM8350_S_CURVE_NONE 0x0 +#define WM8350_S_CURVE_FAST 0x1 +#define WM8350_S_CURVE_MEDIUM 0x2 +#define WM8350_S_CURVE_SLOW 0x3 + +#define WM8350_DISCHARGE_OFF 0x0 +#define WM8350_DISCHARGE_FAST 0x1 +#define WM8350_DISCHARGE_MEDIUM 0x2 +#define WM8350_DISCHARGE_SLOW 0x3 + +#define WM8350_TIE_OFF_500R 0x0 +#define WM8350_TIE_OFF_30K 0x1 + +/* + * Clock sources & directions + */ +#define WM8350_SYSCLK 0 + +#define WM8350_MCLK_SEL_PLL_MCLK 0 +#define WM8350_MCLK_SEL_PLL_DAC 1 +#define WM8350_MCLK_SEL_PLL_ADC 2 +#define WM8350_MCLK_SEL_PLL_32K 3 +#define WM8350_MCLK_SEL_MCLK 5 + +#define WM8350_MCLK_DIR_OUT 0 +#define WM8350_MCLK_DIR_IN 1 + +/* clock divider id's */ +#define WM8350_ADC_CLKDIV 0 +#define WM8350_DAC_CLKDIV 1 +#define WM8350_BCLK_CLKDIV 2 +#define WM8350_OPCLK_CLKDIV 3 +#define WM8350_TO_CLKDIV 4 +#define WM8350_SYS_CLKDIV 5 +#define WM8350_DACLR_CLKDIV 6 +#define WM8350_ADCLR_CLKDIV 7 + +/* ADC clock dividers */ +#define WM8350_ADCDIV_1 0x0 +#define WM8350_ADCDIV_1_5 0x1 +#define WM8350_ADCDIV_2 0x2 +#define WM8350_ADCDIV_3 0x3 +#define WM8350_ADCDIV_4 0x4 +#define WM8350_ADCDIV_5_5 0x5 +#define WM8350_ADCDIV_6 0x6 + +/* ADC clock dividers */ +#define WM8350_DACDIV_1 0x0 +#define WM8350_DACDIV_1_5 0x1 +#define WM8350_DACDIV_2 0x2 +#define WM8350_DACDIV_3 0x3 +#define WM8350_DACDIV_4 0x4 +#define WM8350_DACDIV_5_5 0x5 +#define WM8350_DACDIV_6 0x6 + +/* BCLK clock dividers */ +#define WM8350_BCLK_DIV_1 (0x0 << 4) +#define WM8350_BCLK_DIV_1_5 (0x1 << 4) +#define WM8350_BCLK_DIV_2 (0x2 << 4) +#define WM8350_BCLK_DIV_3 (0x3 << 4) +#define WM8350_BCLK_DIV_4 (0x4 << 4) +#define WM8350_BCLK_DIV_5_5 (0x5 << 4) +#define WM8350_BCLK_DIV_6 (0x6 << 4) +#define WM8350_BCLK_DIV_8 (0x7 << 4) +#define WM8350_BCLK_DIV_11 (0x8 << 4) +#define WM8350_BCLK_DIV_12 (0x9 << 4) +#define WM8350_BCLK_DIV_16 (0xa << 4) +#define WM8350_BCLK_DIV_22 (0xb << 4) +#define WM8350_BCLK_DIV_24 (0xc << 4) +#define WM8350_BCLK_DIV_32 (0xd << 4) +#define WM8350_BCLK_DIV_44 (0xe << 4) +#define WM8350_BCLK_DIV_48 (0xf << 4) + +/* Sys (MCLK) clock dividers */ +#define WM8350_MCLK_DIV_1 (0x0 << 8) +#define WM8350_MCLK_DIV_2 (0x1 << 8) + +/* OP clock dividers */ +#define WM8350_OPCLK_DIV_1 0x0 +#define WM8350_OPCLK_DIV_2 0x1 +#define WM8350_OPCLK_DIV_3 0x2 +#define WM8350_OPCLK_DIV_4 0x3 +#define WM8350_OPCLK_DIV_5_5 0x4 +#define WM8350_OPCLK_DIV_6 0x5 + +/* DAI ID */ +#define WM8350_HIFI_DAI 0 + +/* + * Audio interrupts. + */ +#define WM8350_IRQ_CODEC_JCK_DET_L 39 +#define WM8350_IRQ_CODEC_JCK_DET_R 40 +#define WM8350_IRQ_CODEC_MICSCD 41 +#define WM8350_IRQ_CODEC_MICD 42 + +struct wm8350_codec { + struct platform_device *pdev; +}; + +#endif diff --git a/include/linux/mfd/wm8350/comparator.h b/include/linux/mfd/wm8350/comparator.h new file mode 100644 index 000000000000..053788649452 --- /dev/null +++ b/include/linux/mfd/wm8350/comparator.h @@ -0,0 +1,167 @@ +/* + * comparator.h -- Comparator Aux ADC for Wolfson WM8350 PMIC + * + * Copyright 2007 Wolfson Microelectronics PLC + * + * 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 __LINUX_MFD_WM8350_COMPARATOR_H_ +#define __LINUX_MFD_WM8350_COMPARATOR_H_ + +/* + * Registers + */ + +#define WM8350_DIGITISER_CONTROL_1 0x90 +#define WM8350_DIGITISER_CONTROL_2 0x91 +#define WM8350_AUX1_READBACK 0x98 +#define WM8350_AUX2_READBACK 0x99 +#define WM8350_AUX3_READBACK 0x9A +#define WM8350_AUX4_READBACK 0x9B +#define WM8350_CHIP_TEMP_READBACK 0x9F +#define WM8350_GENERIC_COMPARATOR_CONTROL 0xA3 +#define WM8350_GENERIC_COMPARATOR_1 0xA4 +#define WM8350_GENERIC_COMPARATOR_2 0xA5 +#define WM8350_GENERIC_COMPARATOR_3 0xA6 +#define WM8350_GENERIC_COMPARATOR_4 0xA7 + +/* + * R144 (0x90) - Digitiser Control (1) + */ +#define WM8350_AUXADC_CTC 0x4000 +#define WM8350_AUXADC_POLL 0x2000 +#define WM8350_AUXADC_HIB_MODE 0x1000 +#define WM8350_AUXADC_SEL8 0x0080 +#define WM8350_AUXADC_SEL7 0x0040 +#define WM8350_AUXADC_SEL6 0x0020 +#define WM8350_AUXADC_SEL5 0x0010 +#define WM8350_AUXADC_SEL4 0x0008 +#define WM8350_AUXADC_SEL3 0x0004 +#define WM8350_AUXADC_SEL2 0x0002 +#define WM8350_AUXADC_SEL1 0x0001 + +/* + * R145 (0x91) - Digitiser Control (2) + */ +#define WM8350_AUXADC_MASKMODE_MASK 0x3000 +#define WM8350_AUXADC_CRATE_MASK 0x0700 +#define WM8350_AUXADC_CAL 0x0004 +#define WM8350_AUX_RBMODE 0x0002 +#define WM8350_AUXADC_WAIT 0x0001 + +/* + * R152 (0x98) - AUX1 Readback + */ +#define WM8350_AUXADC_SCALE1_MASK 0x6000 +#define WM8350_AUXADC_REF1 0x1000 +#define WM8350_AUXADC_DATA1_MASK 0x0FFF + +/* + * R153 (0x99) - AUX2 Readback + */ +#define WM8350_AUXADC_SCALE2_MASK 0x6000 +#define WM8350_AUXADC_REF2 0x1000 +#define WM8350_AUXADC_DATA2_MASK 0x0FFF + +/* + * R154 (0x9A) - AUX3 Readback + */ +#define WM8350_AUXADC_SCALE3_MASK 0x6000 +#define WM8350_AUXADC_REF3 0x1000 +#define WM8350_AUXADC_DATA3_MASK 0x0FFF + +/* + * R155 (0x9B) - AUX4 Readback + */ +#define WM8350_AUXADC_SCALE4_MASK 0x6000 +#define WM8350_AUXADC_REF4 0x1000 +#define WM8350_AUXADC_DATA4_MASK 0x0FFF + +/* + * R156 (0x9C) - USB Voltage Readback + */ +#define WM8350_AUXADC_DATA_USB_MASK 0x0FFF + +/* + * R157 (0x9D) - LINE Voltage Readback + */ +#define WM8350_AUXADC_DATA_LINE_MASK 0x0FFF + +/* + * R158 (0x9E) - BATT Voltage Readback + */ +#define WM8350_AUXADC_DATA_BATT_MASK 0x0FFF + +/* + * R159 (0x9F) - Chip Temp Readback + */ +#define WM8350_AUXADC_DATA_CHIPTEMP_MASK 0x0FFF + +/* + * R163 (0xA3) - Generic Comparator Control + */ +#define WM8350_DCMP4_ENA 0x0008 +#define WM8350_DCMP3_ENA 0x0004 +#define WM8350_DCMP2_ENA 0x0002 +#define WM8350_DCMP1_ENA 0x0001 + +/* + * R164 (0xA4) - Generic comparator 1 + */ +#define WM8350_DCMP1_SRCSEL_MASK 0xE000 +#define WM8350_DCMP1_GT 0x1000 +#define WM8350_DCMP1_THR_MASK 0x0FFF + +/* + * R165 (0xA5) - Generic comparator 2 + */ +#define WM8350_DCMP2_SRCSEL_MASK 0xE000 +#define WM8350_DCMP2_GT 0x1000 +#define WM8350_DCMP2_THR_MASK 0x0FFF + +/* + * R166 (0xA6) - Generic comparator 3 + */ +#define WM8350_DCMP3_SRCSEL_MASK 0xE000 +#define WM8350_DCMP3_GT 0x1000 +#define WM8350_DCMP3_THR_MASK 0x0FFF + +/* + * R167 (0xA7) - Generic comparator 4 + */ +#define WM8350_DCMP4_SRCSEL_MASK 0xE000 +#define WM8350_DCMP4_GT 0x1000 +#define WM8350_DCMP4_THR_MASK 0x0FFF + +/* + * Interrupts. + */ +#define WM8350_IRQ_AUXADC_DATARDY 16 +#define WM8350_IRQ_AUXADC_DCOMP4 17 +#define WM8350_IRQ_AUXADC_DCOMP3 18 +#define WM8350_IRQ_AUXADC_DCOMP2 19 +#define WM8350_IRQ_AUXADC_DCOMP1 20 +#define WM8350_IRQ_SYS_HYST_COMP_FAIL 21 +#define WM8350_IRQ_SYS_CHIP_GT115 22 +#define WM8350_IRQ_SYS_CHIP_GT140 23 + +/* + * USB/2, LINE & BATT = ((VRTC * 2) / 4095)) * 10e6 uV + * Where VRTC = 2.7 V + */ +#define WM8350_AUX_COEFF 1319 + +#define WM8350_AUXADC_AUX1 0 +#define WM8350_AUXADC_AUX2 1 +#define WM8350_AUXADC_AUX3 2 +#define WM8350_AUXADC_AUX4 3 +#define WM8350_AUXADC_USB 4 +#define WM8350_AUXADC_LINE 5 +#define WM8350_AUXADC_BATT 6 +#define WM8350_AUXADC_TEMP 7 + +#endif diff --git a/include/linux/mfd/wm8350/core.h b/include/linux/mfd/wm8350/core.h new file mode 100644 index 000000000000..6ebf97f2a475 --- /dev/null +++ b/include/linux/mfd/wm8350/core.h @@ -0,0 +1,631 @@ +/* + * core.h -- Core Driver for Wolfson WM8350 PMIC + * + * Copyright 2007 Wolfson Microelectronics PLC + * + * 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 __LINUX_MFD_WM8350_CORE_H_ +#define __LINUX_MFD_WM8350_CORE_H_ + +#include <linux/kernel.h> +#include <linux/mutex.h> +#include <linux/workqueue.h> + +#include <linux/mfd/wm8350/audio.h> +#include <linux/mfd/wm8350/gpio.h> +#include <linux/mfd/wm8350/pmic.h> +#include <linux/mfd/wm8350/rtc.h> +#include <linux/mfd/wm8350/supply.h> +#include <linux/mfd/wm8350/wdt.h> + +/* + * Register values. + */ +#define WM8350_RESET_ID 0x00 +#define WM8350_ID 0x01 +#define WM8350_SYSTEM_CONTROL_1 0x03 +#define WM8350_SYSTEM_CONTROL_2 0x04 +#define WM8350_SYSTEM_HIBERNATE 0x05 +#define WM8350_INTERFACE_CONTROL 0x06 +#define WM8350_POWER_MGMT_1 0x08 +#define WM8350_POWER_MGMT_2 0x09 +#define WM8350_POWER_MGMT_3 0x0A +#define WM8350_POWER_MGMT_4 0x0B +#define WM8350_POWER_MGMT_5 0x0C +#define WM8350_POWER_MGMT_6 0x0D +#define WM8350_POWER_MGMT_7 0x0E + +#define WM8350_SYSTEM_INTERRUPTS 0x18 +#define WM8350_INT_STATUS_1 0x19 +#define WM8350_INT_STATUS_2 0x1A +#define WM8350_POWER_UP_INT_STATUS 0x1B +#define WM8350_UNDER_VOLTAGE_INT_STATUS 0x1C +#define WM8350_OVER_CURRENT_INT_STATUS 0x1D +#define WM8350_GPIO_INT_STATUS 0x1E +#define WM8350_COMPARATOR_INT_STATUS 0x1F +#define WM8350_SYSTEM_INTERRUPTS_MASK 0x20 +#define WM8350_INT_STATUS_1_MASK 0x21 +#define WM8350_INT_STATUS_2_MASK 0x22 +#define WM8350_POWER_UP_INT_STATUS_MASK 0x23 +#define WM8350_UNDER_VOLTAGE_INT_STATUS_MASK 0x24 +#define WM8350_OVER_CURRENT_INT_STATUS_MASK 0x25 +#define WM8350_GPIO_INT_STATUS_MASK 0x26 +#define WM8350_COMPARATOR_INT_STATUS_MASK 0x27 + +#define WM8350_MAX_REGISTER 0xFF + +/* + * Field Definitions. + */ + +/* + * R0 (0x00) - Reset/ID + */ +#define WM8350_SW_RESET_CHIP_ID_MASK 0xFFFF + +/* + * R1 (0x01) - ID + */ +#define WM8350_CHIP_REV_MASK 0x7000 +#define WM8350_CONF_STS_MASK 0x0C00 +#define WM8350_CUST_ID_MASK 0x00FF + +/* + * R3 (0x03) - System Control 1 + */ +#define WM8350_CHIP_ON 0x8000 +#define WM8350_POWERCYCLE 0x2000 +#define WM8350_VCC_FAULT_OV 0x1000 +#define WM8350_REG_RSTB_TIME_MASK 0x0C00 +#define WM8350_BG_SLEEP 0x0200 +#define WM8350_MEM_VALID 0x0020 +#define WM8350_CHIP_SET_UP 0x0010 +#define WM8350_ON_DEB_T 0x0008 +#define WM8350_ON_POL 0x0002 +#define WM8350_IRQ_POL 0x0001 + +/* + * R4 (0x04) - System Control 2 + */ +#define WM8350_USB_SUSPEND_8MA 0x8000 +#define WM8350_USB_SUSPEND 0x4000 +#define WM8350_USB_MSTR 0x2000 +#define WM8350_USB_MSTR_SRC 0x1000 +#define WM8350_USB_500MA 0x0800 +#define WM8350_USB_NOLIM 0x0400 + +/* + * R5 (0x05) - System Hibernate + */ +#define WM8350_HIBERNATE 0x8000 +#define WM8350_WDOG_HIB_MODE 0x0080 +#define WM8350_REG_HIB_STARTUP_SEQ 0x0040 +#define WM8350_REG_RESET_HIB_MODE 0x0020 +#define WM8350_RST_HIB_MODE 0x0010 +#define WM8350_IRQ_HIB_MODE 0x0008 +#define WM8350_MEMRST_HIB_MODE 0x0004 +#define WM8350_PCCOMP_HIB_MODE 0x0002 +#define WM8350_TEMPMON_HIB_MODE 0x0001 + +/* + * R6 (0x06) - Interface Control + */ +#define WM8350_USE_DEV_PINS 0x8000 +#define WM8350_USE_DEV_PINS_MASK 0x8000 +#define WM8350_USE_DEV_PINS_SHIFT 15 +#define WM8350_DEV_ADDR_MASK 0x6000 +#define WM8350_DEV_ADDR_SHIFT 13 +#define WM8350_CONFIG_DONE 0x1000 +#define WM8350_CONFIG_DONE_MASK 0x1000 +#define WM8350_CONFIG_DONE_SHIFT 12 +#define WM8350_RECONFIG_AT_ON 0x0800 +#define WM8350_RECONFIG_AT_ON_MASK 0x0800 +#define WM8350_RECONFIG_AT_ON_SHIFT 11 +#define WM8350_AUTOINC 0x0200 +#define WM8350_AUTOINC_MASK 0x0200 +#define WM8350_AUTOINC_SHIFT 9 +#define WM8350_ARA 0x0100 +#define WM8350_ARA_MASK 0x0100 +#define WM8350_ARA_SHIFT 8 +#define WM8350_SPI_CFG 0x0008 +#define WM8350_SPI_CFG_MASK 0x0008 +#define WM8350_SPI_CFG_SHIFT 3 +#define WM8350_SPI_4WIRE 0x0004 +#define WM8350_SPI_4WIRE_MASK 0x0004 +#define WM8350_SPI_4WIRE_SHIFT 2 +#define WM8350_SPI_3WIRE 0x0002 +#define WM8350_SPI_3WIRE_MASK 0x0002 +#define WM8350_SPI_3WIRE_SHIFT 1 + +/* Bit values for R06 (0x06) */ +#define WM8350_USE_DEV_PINS_PRIMARY 0 +#define WM8350_USE_DEV_PINS_DEV 1 + +#define WM8350_DEV_ADDR_34 0 +#define WM8350_DEV_ADDR_36 1 +#define WM8350_DEV_ADDR_3C 2 +#define WM8350_DEV_ADDR_3E 3 + +#define WM8350_CONFIG_DONE_OFF 0 +#define WM8350_CONFIG_DONE_DONE 1 + +#define WM8350_RECONFIG_AT_ON_OFF 0 +#define WM8350_RECONFIG_AT_ON_ON 1 + +#define WM8350_AUTOINC_OFF 0 +#define WM8350_AUTOINC_ON 1 + +#define WM8350_ARA_OFF 0 +#define WM8350_ARA_ON 1 + +#define WM8350_SPI_CFG_CMOS 0 +#define WM8350_SPI_CFG_OD 1 + +#define WM8350_SPI_4WIRE_3WIRE 0 +#define WM8350_SPI_4WIRE_4WIRE 1 + +#define WM8350_SPI_3WIRE_I2C 0 +#define WM8350_SPI_3WIRE_SPI 1 + +/* + * R8 (0x08) - Power mgmt (1) + */ +#define WM8350_CODEC_ISEL_MASK 0xC000 +#define WM8350_VBUFEN 0x2000 +#define WM8350_OUTPUT_DRAIN_EN 0x0400 +#define WM8350_MIC_DET_ENA 0x0100 +#define WM8350_BIASEN 0x0020 +#define WM8350_MICBEN 0x0010 +#define WM8350_VMIDEN 0x0004 +#define WM8350_VMID_MASK 0x0003 +#define WM8350_VMID_SHIFT 0 + +/* + * R9 (0x09) - Power mgmt (2) + */ +#define WM8350_IN3R_ENA 0x0800 +#define WM8350_IN3L_ENA 0x0400 +#define WM8350_INR_ENA 0x0200 +#define WM8350_INL_ENA 0x0100 +#define WM8350_MIXINR_ENA 0x0080 +#define WM8350_MIXINL_ENA 0x0040 +#define WM8350_OUT4_ENA 0x0020 +#define WM8350_OUT3_ENA 0x0010 +#define WM8350_MIXOUTR_ENA 0x0002 +#define WM8350_MIXOUTL_ENA 0x0001 + +/* + * R10 (0x0A) - Power mgmt (3) + */ +#define WM8350_IN3R_TO_OUT2R 0x0080 +#define WM8350_OUT2R_ENA 0x0008 +#define WM8350_OUT2L_ENA 0x0004 +#define WM8350_OUT1R_ENA 0x0002 +#define WM8350_OUT1L_ENA 0x0001 + +/* + * R11 (0x0B) - Power mgmt (4) + */ +#define WM8350_SYSCLK_ENA 0x4000 +#define WM8350_ADC_HPF_ENA 0x2000 +#define WM8350_FLL_ENA 0x0800 +#define WM8350_FLL_OSC_ENA 0x0400 +#define WM8350_TOCLK_ENA 0x0100 +#define WM8350_DACR_ENA 0x0020 +#define WM8350_DACL_ENA 0x0010 +#define WM8350_ADCR_ENA 0x0008 +#define WM8350_ADCL_ENA 0x0004 + +/* + * R12 (0x0C) - Power mgmt (5) + */ +#define WM8350_CODEC_ENA 0x1000 +#define WM8350_RTC_TICK_ENA 0x0800 +#define WM8350_OSC32K_ENA 0x0400 +#define WM8350_CHG_ENA 0x0200 +#define WM8350_ACC_DET_ENA 0x0100 +#define WM8350_AUXADC_ENA 0x0080 +#define WM8350_DCMP4_ENA 0x0008 +#define WM8350_DCMP3_ENA 0x0004 +#define WM8350_DCMP2_ENA 0x0002 +#define WM8350_DCMP1_ENA 0x0001 + +/* + * R13 (0x0D) - Power mgmt (6) + */ +#define WM8350_LS_ENA 0x8000 +#define WM8350_LDO4_ENA 0x0800 +#define WM8350_LDO3_ENA 0x0400 +#define WM8350_LDO2_ENA 0x0200 +#define WM8350_LDO1_ENA 0x0100 +#define WM8350_DC6_ENA 0x0020 +#define WM8350_DC5_ENA 0x0010 +#define WM8350_DC4_ENA 0x0008 +#define WM8350_DC3_ENA 0x0004 +#define WM8350_DC2_ENA 0x0002 +#define WM8350_DC1_ENA 0x0001 + +/* + * R14 (0x0E) - Power mgmt (7) + */ +#define WM8350_CS2_ENA 0x0002 +#define WM8350_CS1_ENA 0x0001 + +/* + * R24 (0x18) - System Interrupts + */ +#define WM8350_OC_INT 0x2000 +#define WM8350_UV_INT 0x1000 +#define WM8350_PUTO_INT 0x0800 +#define WM8350_CS_INT 0x0200 +#define WM8350_EXT_INT 0x0100 +#define WM8350_CODEC_INT 0x0080 +#define WM8350_GP_INT 0x0040 +#define WM8350_AUXADC_INT 0x0020 +#define WM8350_RTC_INT 0x0010 +#define WM8350_SYS_INT 0x0008 +#define WM8350_CHG_INT 0x0004 +#define WM8350_USB_INT 0x0002 +#define WM8350_WKUP_INT 0x0001 + +/* + * R25 (0x19) - Interrupt Status 1 + */ +#define WM8350_CHG_BAT_HOT_EINT 0x8000 +#define WM8350_CHG_BAT_COLD_EINT 0x4000 +#define WM8350_CHG_BAT_FAIL_EINT 0x2000 +#define WM8350_CHG_TO_EINT 0x1000 +#define WM8350_CHG_END_EINT 0x0800 +#define WM8350_CHG_START_EINT 0x0400 +#define WM8350_CHG_FAST_RDY_EINT 0x0200 +#define WM8350_RTC_PER_EINT 0x0080 +#define WM8350_RTC_SEC_EINT 0x0040 +#define WM8350_RTC_ALM_EINT 0x0020 +#define WM8350_CHG_VBATT_LT_3P9_EINT 0x0004 +#define WM8350_CHG_VBATT_LT_3P1_EINT 0x0002 +#define WM8350_CHG_VBATT_LT_2P85_EINT 0x0001 + +/* + * R26 (0x1A) - Interrupt Status 2 + */ +#define WM8350_CS1_EINT 0x2000 +#define WM8350_CS2_EINT 0x1000 +#define WM8350_USB_LIMIT_EINT 0x0400 +#define WM8350_AUXADC_DATARDY_EINT 0x0100 +#define WM8350_AUXADC_DCOMP4_EINT 0x0080 +#define WM8350_AUXADC_DCOMP3_EINT 0x0040 +#define WM8350_AUXADC_DCOMP2_EINT 0x0020 +#define WM8350_AUXADC_DCOMP1_EINT 0x0010 +#define WM8350_SYS_HYST_COMP_FAIL_EINT 0x0008 +#define WM8350_SYS_CHIP_GT115_EINT 0x0004 +#define WM8350_SYS_CHIP_GT140_EINT 0x0002 +#define WM8350_SYS_WDOG_TO_EINT 0x0001 + +/* + * R27 (0x1B) - Power Up Interrupt Status + */ +#define WM8350_PUTO_LDO4_EINT 0x0800 +#define WM8350_PUTO_LDO3_EINT 0x0400 +#define WM8350_PUTO_LDO2_EINT 0x0200 +#define WM8350_PUTO_LDO1_EINT 0x0100 +#define WM8350_PUTO_DC6_EINT 0x0020 +#define WM8350_PUTO_DC5_EINT 0x0010 +#define WM8350_PUTO_DC4_EINT 0x0008 +#define WM8350_PUTO_DC3_EINT 0x0004 +#define WM8350_PUTO_DC2_EINT 0x0002 +#define WM8350_PUTO_DC1_EINT 0x0001 + +/* + * R28 (0x1C) - Under Voltage Interrupt status + */ +#define WM8350_UV_LDO4_EINT 0x0800 +#define WM8350_UV_LDO3_EINT 0x0400 +#define WM8350_UV_LDO2_EINT 0x0200 +#define WM8350_UV_LDO1_EINT 0x0100 +#define WM8350_UV_DC6_EINT 0x0020 +#define WM8350_UV_DC5_EINT 0x0010 +#define WM8350_UV_DC4_EINT 0x0008 +#define WM8350_UV_DC3_EINT 0x0004 +#define WM8350_UV_DC2_EINT 0x0002 +#define WM8350_UV_DC1_EINT 0x0001 + +/* + * R29 (0x1D) - Over Current Interrupt status + */ +#define WM8350_OC_LS_EINT 0x8000 + +/* + * R30 (0x1E) - GPIO Interrupt Status + */ +#define WM8350_GP12_EINT 0x1000 +#define WM8350_GP11_EINT 0x0800 +#define WM8350_GP10_EINT 0x0400 +#define WM8350_GP9_EINT 0x0200 +#define WM8350_GP8_EINT 0x0100 +#define WM8350_GP7_EINT 0x0080 +#define WM8350_GP6_EINT 0x0040 +#define WM8350_GP5_EINT 0x0020 +#define WM8350_GP4_EINT 0x0010 +#define WM8350_GP3_EINT 0x0008 +#define WM8350_GP2_EINT 0x0004 +#define WM8350_GP1_EINT 0x0002 +#define WM8350_GP0_EINT 0x0001 + +/* + * R31 (0x1F) - Comparator Interrupt Status + */ +#define WM8350_EXT_USB_FB_EINT 0x8000 +#define WM8350_EXT_WALL_FB_EINT 0x4000 +#define WM8350_EXT_BAT_FB_EINT 0x2000 +#define WM8350_CODEC_JCK_DET_L_EINT 0x0800 +#define WM8350_CODEC_JCK_DET_R_EINT 0x0400 +#define WM8350_CODEC_MICSCD_EINT 0x0200 +#define WM8350_CODEC_MICD_EINT 0x0100 +#define WM8350_WKUP_OFF_STATE_EINT 0x0040 +#define WM8350_WKUP_HIB_STATE_EINT 0x0020 +#define WM8350_WKUP_CONV_FAULT_EINT 0x0010 +#define WM8350_WKUP_WDOG_RST_EINT 0x0008 +#define WM8350_WKUP_GP_PWR_ON_EINT 0x0004 +#define WM8350_WKUP_ONKEY_EINT 0x0002 +#define WM8350_WKUP_GP_WAKEUP_EINT 0x0001 + +/* + * R32 (0x20) - System Interrupts Mask + */ +#define WM8350_IM_OC_INT 0x2000 +#define WM8350_IM_UV_INT 0x1000 +#define WM8350_IM_PUTO_INT 0x0800 +#define WM8350_IM_SPARE_INT 0x0400 +#define WM8350_IM_CS_INT 0x0200 +#define WM8350_IM_EXT_INT 0x0100 +#define WM8350_IM_CODEC_INT 0x0080 +#define WM8350_IM_GP_INT 0x0040 +#define WM8350_IM_AUXADC_INT 0x0020 +#define WM8350_IM_RTC_INT 0x0010 +#define WM8350_IM_SYS_INT 0x0008 +#define WM8350_IM_CHG_INT 0x0004 +#define WM8350_IM_USB_INT 0x0002 +#define WM8350_IM_WKUP_INT 0x0001 + +/* + * R33 (0x21) - Interrupt Status 1 Mask + */ +#define WM8350_IM_CHG_BAT_HOT_EINT 0x8000 +#define WM8350_IM_CHG_BAT_COLD_EINT 0x4000 +#define WM8350_IM_CHG_BAT_FAIL_EINT 0x2000 +#define WM8350_IM_CHG_TO_EINT 0x1000 +#define WM8350_IM_CHG_END_EINT 0x0800 +#define WM8350_IM_CHG_START_EINT 0x0400 +#define WM8350_IM_CHG_FAST_RDY_EINT 0x0200 +#define WM8350_IM_RTC_PER_EINT 0x0080 +#define WM8350_IM_RTC_SEC_EINT 0x0040 +#define WM8350_IM_RTC_ALM_EINT 0x0020 +#define WM8350_IM_CHG_VBATT_LT_3P9_EINT 0x0004 +#define WM8350_IM_CHG_VBATT_LT_3P1_EINT 0x0002 +#define WM8350_IM_CHG_VBATT_LT_2P85_EINT 0x0001 + +/* + * R34 (0x22) - Interrupt Status 2 Mask + */ +#define WM8350_IM_SPARE2_EINT 0x8000 +#define WM8350_IM_SPARE1_EINT 0x4000 +#define WM8350_IM_CS1_EINT 0x2000 +#define WM8350_IM_CS2_EINT 0x1000 +#define WM8350_IM_USB_LIMIT_EINT 0x0400 +#define WM8350_IM_AUXADC_DATARDY_EINT 0x0100 +#define WM8350_IM_AUXADC_DCOMP4_EINT 0x0080 +#define WM8350_IM_AUXADC_DCOMP3_EINT 0x0040 +#define WM8350_IM_AUXADC_DCOMP2_EINT 0x0020 +#define WM8350_IM_AUXADC_DCOMP1_EINT 0x0010 +#define WM8350_IM_SYS_HYST_COMP_FAIL_EINT 0x0008 +#define WM8350_IM_SYS_CHIP_GT115_EINT 0x0004 +#define WM8350_IM_SYS_CHIP_GT140_EINT 0x0002 +#define WM8350_IM_SYS_WDOG_TO_EINT 0x0001 + +/* + * R35 (0x23) - Power Up Interrupt Status Mask + */ +#define WM8350_IM_PUTO_LDO4_EINT 0x0800 +#define WM8350_IM_PUTO_LDO3_EINT 0x0400 +#define WM8350_IM_PUTO_LDO2_EINT 0x0200 +#define WM8350_IM_PUTO_LDO1_EINT 0x0100 +#define WM8350_IM_PUTO_DC6_EINT 0x0020 +#define WM8350_IM_PUTO_DC5_EINT 0x0010 +#define WM8350_IM_PUTO_DC4_EINT 0x0008 +#define WM8350_IM_PUTO_DC3_EINT 0x0004 +#define WM8350_IM_PUTO_DC2_EINT 0x0002 +#define WM8350_IM_PUTO_DC1_EINT 0x0001 + +/* + * R36 (0x24) - Under Voltage Interrupt status Mask + */ +#define WM8350_IM_UV_LDO4_EINT 0x0800 +#define WM8350_IM_UV_LDO3_EINT 0x0400 +#define WM8350_IM_UV_LDO2_EINT 0x0200 +#define WM8350_IM_UV_LDO1_EINT 0x0100 +#define WM8350_IM_UV_DC6_EINT 0x0020 +#define WM8350_IM_UV_DC5_EINT 0x0010 +#define WM8350_IM_UV_DC4_EINT 0x0008 +#define WM8350_IM_UV_DC3_EINT 0x0004 +#define WM8350_IM_UV_DC2_EINT 0x0002 +#define WM8350_IM_UV_DC1_EINT 0x0001 + +/* + * R37 (0x25) - Over Current Interrupt status Mask + */ +#define WM8350_IM_OC_LS_EINT 0x8000 + +/* + * R38 (0x26) - GPIO Interrupt Status Mask + */ +#define WM8350_IM_GP12_EINT 0x1000 +#define WM8350_IM_GP11_EINT 0x0800 +#define WM8350_IM_GP10_EINT 0x0400 +#define WM8350_IM_GP9_EINT 0x0200 +#define WM8350_IM_GP8_EINT 0x0100 +#define WM8350_IM_GP7_EINT 0x0080 +#define WM8350_IM_GP6_EINT 0x0040 +#define WM8350_IM_GP5_EINT 0x0020 +#define WM8350_IM_GP4_EINT 0x0010 +#define WM8350_IM_GP3_EINT 0x0008 +#define WM8350_IM_GP2_EINT 0x0004 +#define WM8350_IM_GP1_EINT 0x0002 +#define WM8350_IM_GP0_EINT 0x0001 + +/* + * R39 (0x27) - Comparator Interrupt Status Mask + */ +#define WM8350_IM_EXT_USB_FB_EINT 0x8000 +#define WM8350_IM_EXT_WALL_FB_EINT 0x4000 +#define WM8350_IM_EXT_BAT_FB_EINT 0x2000 +#define WM8350_IM_CODEC_JCK_DET_L_EINT 0x0800 +#define WM8350_IM_CODEC_JCK_DET_R_EINT 0x0400 +#define WM8350_IM_CODEC_MICSCD_EINT 0x0200 +#define WM8350_IM_CODEC_MICD_EINT 0x0100 +#define WM8350_IM_WKUP_OFF_STATE_EINT 0x0040 +#define WM8350_IM_WKUP_HIB_STATE_EINT 0x0020 +#define WM8350_IM_WKUP_CONV_FAULT_EINT 0x0010 +#define WM8350_IM_WKUP_WDOG_RST_EINT 0x0008 +#define WM8350_IM_WKUP_GP_PWR_ON_EINT 0x0004 +#define WM8350_IM_WKUP_ONKEY_EINT 0x0002 +#define WM8350_IM_WKUP_GP_WAKEUP_EINT 0x0001 + +/* + * R220 (0xDC) - RAM BIST 1 + */ +#define WM8350_READ_STATUS 0x0800 +#define WM8350_TSTRAM_CLK 0x0100 +#define WM8350_TSTRAM_CLK_ENA 0x0080 +#define WM8350_STARTSEQ 0x0040 +#define WM8350_READ_SRC 0x0020 +#define WM8350_COUNT_DIR 0x0010 +#define WM8350_TSTRAM_MODE_MASK 0x000E +#define WM8350_TSTRAM_ENA 0x0001 + +/* + * R225 (0xE1) - DCDC/LDO status + */ +#define WM8350_LS_STS 0x8000 +#define WM8350_LDO4_STS 0x0800 +#define WM8350_LDO3_STS 0x0400 +#define WM8350_LDO2_STS 0x0200 +#define WM8350_LDO1_STS 0x0100 +#define WM8350_DC6_STS 0x0020 +#define WM8350_DC5_STS 0x0010 +#define WM8350_DC4_STS 0x0008 +#define WM8350_DC3_STS 0x0004 +#define WM8350_DC2_STS 0x0002 +#define WM8350_DC1_STS 0x0001 + +/* WM8350 wake up conditions */ +#define WM8350_IRQ_WKUP_OFF_STATE 43 +#define WM8350_IRQ_WKUP_HIB_STATE 44 +#define WM8350_IRQ_WKUP_CONV_FAULT 45 +#define WM8350_IRQ_WKUP_WDOG_RST 46 +#define WM8350_IRQ_WKUP_GP_PWR_ON 47 +#define WM8350_IRQ_WKUP_ONKEY 48 +#define WM8350_IRQ_WKUP_GP_WAKEUP 49 + +/* wm8350 chip revisions */ +#define WM8350_REV_E 0x4 +#define WM8350_REV_F 0x5 +#define WM8350_REV_G 0x6 + +#define WM8350_NUM_IRQ 63 + +struct wm8350_reg_access { + u16 readable; /* Mask of readable bits */ + u16 writable; /* Mask of writable bits */ + u16 vol; /* Mask of volatile bits */ +}; +extern const struct wm8350_reg_access wm8350_reg_io_map[]; +extern const u16 wm8350_mode0_defaults[]; +extern const u16 wm8350_mode1_defaults[]; +extern const u16 wm8350_mode2_defaults[]; +extern const u16 wm8350_mode3_defaults[]; + +struct wm8350; + +struct wm8350_irq { + void (*handler) (struct wm8350 *, int, void *); + void *data; +}; + +struct wm8350 { + int rev; /* chip revision */ + + struct device *dev; + + /* device IO */ + union { + struct i2c_client *i2c_client; + struct spi_device *spi_device; + }; + int (*read_dev)(struct wm8350 *wm8350, char reg, int size, void *dest); + int (*write_dev)(struct wm8350 *wm8350, char reg, int size, + void *src); + u16 *reg_cache; + + /* Interrupt handling */ + struct work_struct irq_work; + struct mutex irq_mutex; /* IRQ table mutex */ + struct wm8350_irq irq[WM8350_NUM_IRQ]; + int chip_irq; + + /* Client devices */ + struct wm8350_codec codec; + struct wm8350_gpio gpio; + struct wm8350_pmic pmic; + struct wm8350_power power; + struct wm8350_rtc rtc; + struct wm8350_wdt wdt; +}; + +/** + * Data to be supplied by the platform to initialise the WM8350. + * + * @init: Function called during driver initialisation. Should be + * used by the platform to configure GPIO functions and similar. + */ +struct wm8350_platform_data { + int (*init)(struct wm8350 *wm8350); +}; + + +/* + * WM8350 device initialisation and exit. + */ +int wm8350_device_init(struct wm8350 *wm8350, int irq, + struct wm8350_platform_data *pdata); +void wm8350_device_exit(struct wm8350 *wm8350); + +/* + * WM8350 device IO + */ +int wm8350_clear_bits(struct wm8350 *wm8350, u16 reg, u16 mask); +int wm8350_set_bits(struct wm8350 *wm8350, u16 reg, u16 mask); +u16 wm8350_reg_read(struct wm8350 *wm8350, int reg); +int wm8350_reg_write(struct wm8350 *wm8350, int reg, u16 val); +int wm8350_reg_lock(struct wm8350 *wm8350); +int wm8350_reg_unlock(struct wm8350 *wm8350); +int wm8350_block_read(struct wm8350 *wm8350, int reg, int size, u16 *dest); +int wm8350_block_write(struct wm8350 *wm8350, int reg, int size, u16 *src); + +/* + * WM8350 internal interrupts + */ +int wm8350_register_irq(struct wm8350 *wm8350, int irq, + void (*handler) (struct wm8350 *, int, void *), + void *data); +int wm8350_free_irq(struct wm8350 *wm8350, int irq); +int wm8350_mask_irq(struct wm8350 *wm8350, int irq); +int wm8350_unmask_irq(struct wm8350 *wm8350, int irq); + + +#endif diff --git a/include/linux/mfd/wm8350/gpio.h b/include/linux/mfd/wm8350/gpio.h new file mode 100644 index 000000000000..ed91e8f5d298 --- /dev/null +++ b/include/linux/mfd/wm8350/gpio.h @@ -0,0 +1,342 @@ +/* + * gpio.h -- GPIO Driver for Wolfson WM8350 PMIC + * + * Copyright 2007 Wolfson Microelectronics PLC + * + * 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 __LINUX_MFD_WM8350_GPIO_H_ +#define __LINUX_MFD_WM8350_GPIO_H_ + +#include <linux/platform_device.h> + +/* + * GPIO Registers. + */ +#define WM8350_GPIO_DEBOUNCE 0x80 +#define WM8350_GPIO_PIN_PULL_UP_CONTROL 0x81 +#define WM8350_GPIO_PULL_DOWN_CONTROL 0x82 +#define WM8350_GPIO_INT_MODE 0x83 +#define WM8350_GPIO_CONTROL 0x85 +#define WM8350_GPIO_CONFIGURATION_I_O 0x86 +#define WM8350_GPIO_PIN_POLARITY_TYPE 0x87 +#define WM8350_GPIO_FUNCTION_SELECT_1 0x8C +#define WM8350_GPIO_FUNCTION_SELECT_2 0x8D +#define WM8350_GPIO_FUNCTION_SELECT_3 0x8E +#define WM8350_GPIO_FUNCTION_SELECT_4 0x8F + +/* + * GPIO Functions + */ +#define WM8350_GPIO0_GPIO_IN 0x0 +#define WM8350_GPIO0_GPIO_OUT 0x0 +#define WM8350_GPIO0_PWR_ON_IN 0x1 +#define WM8350_GPIO0_PWR_ON_OUT 0x1 +#define WM8350_GPIO0_LDO_EN_IN 0x2 +#define WM8350_GPIO0_VRTC_OUT 0x2 +#define WM8350_GPIO0_LPWR1_IN 0x3 +#define WM8350_GPIO0_POR_B_OUT 0x3 + +#define WM8350_GPIO1_GPIO_IN 0x0 +#define WM8350_GPIO1_GPIO_OUT 0x0 +#define WM8350_GPIO1_PWR_ON_IN 0x1 +#define WM8350_GPIO1_DO_CONF_OUT 0x1 +#define WM8350_GPIO1_LDO_EN_IN 0x2 +#define WM8350_GPIO1_RESET_OUT 0x2 +#define WM8350_GPIO1_LPWR2_IN 0x3 +#define WM8350_GPIO1_MEMRST_OUT 0x3 + +#define WM8350_GPIO2_GPIO_IN 0x0 +#define WM8350_GPIO2_GPIO_OUT 0x0 +#define WM8350_GPIO2_PWR_ON_IN 0x1 +#define WM8350_GPIO2_PWR_ON_OUT 0x1 +#define WM8350_GPIO2_WAKE_UP_IN 0x2 +#define WM8350_GPIO2_VRTC_OUT 0x2 +#define WM8350_GPIO2_32KHZ_IN 0x3 +#define WM8350_GPIO2_32KHZ_OUT 0x3 + +#define WM8350_GPIO3_GPIO_IN 0x0 +#define WM8350_GPIO3_GPIO_OUT 0x0 +#define WM8350_GPIO3_PWR_ON_IN 0x1 +#define WM8350_GPIO3_P_CLK_OUT 0x1 +#define WM8350_GPIO3_LDO_EN_IN 0x2 +#define WM8350_GPIO3_VRTC_OUT 0x2 +#define WM8350_GPIO3_PWR_OFF_IN 0x3 +#define WM8350_GPIO3_32KHZ_OUT 0x3 + +#define WM8350_GPIO4_GPIO_IN 0x0 +#define WM8350_GPIO4_GPIO_OUT 0x0 +#define WM8350_GPIO4_MR_IN 0x1 +#define WM8350_GPIO4_MEM_RST_OUT 0x1 +#define WM8350_GPIO4_FLASH_IN 0x2 +#define WM8350_GPIO4_ADA_OUT 0x2 +#define WM8350_GPIO4_HIBERNATE_IN 0x3 +#define WM8350_GPIO4_FLASH_OUT 0x3 +#define WM8350_GPIO4_MICDET_OUT 0x4 +#define WM8350_GPIO4_MICSHT_OUT 0x5 + +#define WM8350_GPIO5_GPIO_IN 0x0 +#define WM8350_GPIO5_GPIO_OUT 0x0 +#define WM8350_GPIO5_LPWR1_IN 0x1 +#define WM8350_GPIO5_P_CLK_OUT 0x1 +#define WM8350_GPIO5_ADCLRCLK_IN 0x2 +#define WM8350_GPIO5_ADCLRCLK_OUT 0x2 +#define WM8350_GPIO5_HIBERNATE_IN 0x3 +#define WM8350_GPIO5_32KHZ_OUT 0x3 +#define WM8350_GPIO5_MICDET_OUT 0x4 +#define WM8350_GPIO5_MICSHT_OUT 0x5 +#define WM8350_GPIO5_ADA_OUT 0x6 +#define WM8350_GPIO5_OPCLK_OUT 0x7 + +#define WM8350_GPIO6_GPIO_IN 0x0 +#define WM8350_GPIO6_GPIO_OUT 0x0 +#define WM8350_GPIO6_LPWR2_IN 0x1 +#define WM8350_GPIO6_MEMRST_OUT 0x1 +#define WM8350_GPIO6_FLASH_IN 0x2 +#define WM8350_GPIO6_ADA_OUT 0x2 +#define WM8350_GPIO6_HIBERNATE_IN 0x3 +#define WM8350_GPIO6_RTC_OUT 0x3 +#define WM8350_GPIO6_MICDET_OUT 0x4 +#define WM8350_GPIO6_MICSHT_OUT 0x5 +#define WM8350_GPIO6_ADCLRCLKB_OUT 0x6 +#define WM8350_GPIO6_SDOUT_OUT 0x7 + +#define WM8350_GPIO7_GPIO_IN 0x0 +#define WM8350_GPIO7_GPIO_OUT 0x0 +#define WM8350_GPIO7_LPWR3_IN 0x1 +#define WM8350_GPIO7_P_CLK_OUT 0x1 +#define WM8350_GPIO7_MASK_IN 0x2 +#define WM8350_GPIO7_VCC_FAULT_OUT 0x2 +#define WM8350_GPIO7_HIBERNATE_IN 0x3 +#define WM8350_GPIO7_BATT_FAULT_OUT 0x3 +#define WM8350_GPIO7_MICDET_OUT 0x4 +#define WM8350_GPIO7_MICSHT_OUT 0x5 +#define WM8350_GPIO7_ADA_OUT 0x6 +#define WM8350_GPIO7_CSB_IN 0x7 + +#define WM8350_GPIO8_GPIO_IN 0x0 +#define WM8350_GPIO8_GPIO_OUT 0x0 +#define WM8350_GPIO8_MR_IN 0x1 +#define WM8350_GPIO8_VCC_FAULT_OUT 0x1 +#define WM8350_GPIO8_ADCBCLK_IN 0x2 +#define WM8350_GPIO8_ADCBCLK_OUT 0x2 +#define WM8350_GPIO8_PWR_OFF_IN 0x3 +#define WM8350_GPIO8_BATT_FAULT_OUT 0x3 +#define WM8350_GPIO8_ALTSCL_IN 0xf + +#define WM8350_GPIO9_GPIO_IN 0x0 +#define WM8350_GPIO9_GPIO_OUT 0x0 +#define WM8350_GPIO9_HEARTBEAT_IN 0x1 +#define WM8350_GPIO9_VCC_FAULT_OUT 0x1 +#define WM8350_GPIO9_MASK_IN 0x2 +#define WM8350_GPIO9_LINE_GT_BATT_OUT 0x2 +#define WM8350_GPIO9_PWR_OFF_IN 0x3 +#define WM8350_GPIO9_BATT_FAULT_OUT 0x3 +#define WM8350_GPIO9_ALTSDA_OUT 0xf + +#define WM8350_GPIO10_GPIO_IN 0x0 +#define WM8350_GPIO10_GPIO_OUT 0x0 +#define WM8350_GPIO10_ISINKC_OUT 0x1 +#define WM8350_GPIO10_PWR_OFF_IN 0x2 +#define WM8350_GPIO10_LINE_GT_BATT_OUT 0x2 +#define WM8350_GPIO10_CHD_IND_IN 0x3 + +#define WM8350_GPIO11_GPIO_IN 0x0 +#define WM8350_GPIO11_GPIO_OUT 0x0 +#define WM8350_GPIO11_ISINKD_OUT 0x1 +#define WM8350_GPIO11_WAKEUP_IN 0x2 +#define WM8350_GPIO11_LINE_GT_BATT_OUT 0x2 +#define WM8350_GPIO11_CHD_IND_IN 0x3 + +#define WM8350_GPIO12_GPIO_IN 0x0 +#define WM8350_GPIO12_GPIO_OUT 0x0 +#define WM8350_GPIO12_ISINKE_OUT 0x1 +#define WM8350_GPIO12_LINE_GT_BATT_OUT 0x2 +#define WM8350_GPIO12_LINE_EN_OUT 0x3 +#define WM8350_GPIO12_32KHZ_OUT 0x4 + +#define WM8350_GPIO_DIR_IN 0 +#define WM8350_GPIO_DIR_OUT 1 +#define WM8350_GPIO_ACTIVE_LOW 0 +#define WM8350_GPIO_ACTIVE_HIGH 1 +#define WM8350_GPIO_PULL_NONE 0 +#define WM8350_GPIO_PULL_UP 1 +#define WM8350_GPIO_PULL_DOWN 2 +#define WM8350_GPIO_INVERT_OFF 0 +#define WM8350_GPIO_INVERT_ON 1 +#define WM8350_GPIO_DEBOUNCE_OFF 0 +#define WM8350_GPIO_DEBOUNCE_ON 1 + +/* + * R128 (0x80) - GPIO Debounce + */ +#define WM8350_GP12_DB 0x1000 +#define WM8350_GP11_DB 0x0800 +#define WM8350_GP10_DB 0x0400 +#define WM8350_GP9_DB 0x0200 +#define WM8350_GP8_DB 0x0100 +#define WM8350_GP7_DB 0x0080 +#define WM8350_GP6_DB 0x0040 +#define WM8350_GP5_DB 0x0020 +#define WM8350_GP4_DB 0x0010 +#define WM8350_GP3_DB 0x0008 +#define WM8350_GP2_DB 0x0004 +#define WM8350_GP1_DB 0x0002 +#define WM8350_GP0_DB 0x0001 + +/* + * R129 (0x81) - GPIO Pin pull up Control + */ +#define WM8350_GP12_PU 0x1000 +#define WM8350_GP11_PU 0x0800 +#define WM8350_GP10_PU 0x0400 +#define WM8350_GP9_PU 0x0200 +#define WM8350_GP8_PU 0x0100 +#define WM8350_GP7_PU 0x0080 +#define WM8350_GP6_PU 0x0040 +#define WM8350_GP5_PU 0x0020 +#define WM8350_GP4_PU 0x0010 +#define WM8350_GP3_PU 0x0008 +#define WM8350_GP2_PU 0x0004 +#define WM8350_GP1_PU 0x0002 +#define WM8350_GP0_PU 0x0001 + +/* + * R130 (0x82) - GPIO Pull down Control + */ +#define WM8350_GP12_PD 0x1000 +#define WM8350_GP11_PD 0x0800 +#define WM8350_GP10_PD 0x0400 +#define WM8350_GP9_PD 0x0200 +#define WM8350_GP8_PD 0x0100 +#define WM8350_GP7_PD 0x0080 +#define WM8350_GP6_PD 0x0040 +#define WM8350_GP5_PD 0x0020 +#define WM8350_GP4_PD 0x0010 +#define WM8350_GP3_PD 0x0008 +#define WM8350_GP2_PD 0x0004 +#define WM8350_GP1_PD 0x0002 +#define WM8350_GP0_PD 0x0001 + +/* + * R131 (0x83) - GPIO Interrupt Mode + */ +#define WM8350_GP12_INTMODE 0x1000 +#define WM8350_GP11_INTMODE 0x0800 +#define WM8350_GP10_INTMODE 0x0400 +#define WM8350_GP9_INTMODE 0x0200 +#define WM8350_GP8_INTMODE 0x0100 +#define WM8350_GP7_INTMODE 0x0080 +#define WM8350_GP6_INTMODE 0x0040 +#define WM8350_GP5_INTMODE 0x0020 +#define WM8350_GP4_INTMODE 0x0010 +#define WM8350_GP3_INTMODE 0x0008 +#define WM8350_GP2_INTMODE 0x0004 +#define WM8350_GP1_INTMODE 0x0002 +#define WM8350_GP0_INTMODE 0x0001 + +/* + * R133 (0x85) - GPIO Control + */ +#define WM8350_GP_DBTIME_MASK 0x00C0 + +/* + * R134 (0x86) - GPIO Configuration (i/o) + */ +#define WM8350_GP12_DIR 0x1000 +#define WM8350_GP11_DIR 0x0800 +#define WM8350_GP10_DIR 0x0400 +#define WM8350_GP9_DIR 0x0200 +#define WM8350_GP8_DIR 0x0100 +#define WM8350_GP7_DIR 0x0080 +#define WM8350_GP6_DIR 0x0040 +#define WM8350_GP5_DIR 0x0020 +#define WM8350_GP4_DIR 0x0010 +#define WM8350_GP3_DIR 0x0008 +#define WM8350_GP2_DIR 0x0004 +#define WM8350_GP1_DIR 0x0002 +#define WM8350_GP0_DIR 0x0001 + +/* + * R135 (0x87) - GPIO Pin Polarity / Type + */ +#define WM8350_GP12_CFG 0x1000 +#define WM8350_GP11_CFG 0x0800 +#define WM8350_GP10_CFG 0x0400 +#define WM8350_GP9_CFG 0x0200 +#define WM8350_GP8_CFG 0x0100 +#define WM8350_GP7_CFG 0x0080 +#define WM8350_GP6_CFG 0x0040 +#define WM8350_GP5_CFG 0x0020 +#define WM8350_GP4_CFG 0x0010 +#define WM8350_GP3_CFG 0x0008 +#define WM8350_GP2_CFG 0x0004 +#define WM8350_GP1_CFG 0x0002 +#define WM8350_GP0_CFG 0x0001 + +/* + * R140 (0x8C) - GPIO Function Select 1 + */ +#define WM8350_GP3_FN_MASK 0xF000 +#define WM8350_GP2_FN_MASK 0x0F00 +#define WM8350_GP1_FN_MASK 0x00F0 +#define WM8350_GP0_FN_MASK 0x000F + +/* + * R141 (0x8D) - GPIO Function Select 2 + */ +#define WM8350_GP7_FN_MASK 0xF000 +#define WM8350_GP6_FN_MASK 0x0F00 +#define WM8350_GP5_FN_MASK 0x00F0 +#define WM8350_GP4_FN_MASK 0x000F + +/* + * R142 (0x8E) - GPIO Function Select 3 + */ +#define WM8350_GP11_FN_MASK 0xF000 +#define WM8350_GP10_FN_MASK 0x0F00 +#define WM8350_GP9_FN_MASK 0x00F0 +#define WM8350_GP8_FN_MASK 0x000F + +/* + * R143 (0x8F) - GPIO Function Select 4 + */ +#define WM8350_GP12_FN_MASK 0x000F + +/* + * R230 (0xE6) - GPIO Pin Status + */ +#define WM8350_GP12_LVL 0x1000 +#define WM8350_GP11_LVL 0x0800 +#define WM8350_GP10_LVL 0x0400 +#define WM8350_GP9_LVL 0x0200 +#define WM8350_GP8_LVL 0x0100 +#define WM8350_GP7_LVL 0x0080 +#define WM8350_GP6_LVL 0x0040 +#define WM8350_GP5_LVL 0x0020 +#define WM8350_GP4_LVL 0x0010 +#define WM8350_GP3_LVL 0x0008 +#define WM8350_GP2_LVL 0x0004 +#define WM8350_GP1_LVL 0x0002 +#define WM8350_GP0_LVL 0x0001 + +struct wm8350; + +int wm8350_gpio_config(struct wm8350 *wm8350, int gpio, int dir, int func, + int pol, int pull, int invert, int debounce); + +struct wm8350_gpio { + struct platform_device *pdev; +}; + +/* + * GPIO Interrupts + */ +#define WM8350_IRQ_GPIO(x) (50 + x) + +#endif diff --git a/include/linux/mfd/wm8350/pmic.h b/include/linux/mfd/wm8350/pmic.h new file mode 100644 index 000000000000..69b69e07f62f --- /dev/null +++ b/include/linux/mfd/wm8350/pmic.h @@ -0,0 +1,741 @@ +/* + * pmic.h -- Power Managment Driver for Wolfson WM8350 PMIC + * + * Copyright 2007 Wolfson Microelectronics PLC + * + * 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 __LINUX_MFD_WM8350_PMIC_H +#define __LINUX_MFD_WM8350_PMIC_H + +/* + * Register values. + */ + +#define WM8350_CURRENT_SINK_DRIVER_A 0xAC +#define WM8350_CSA_FLASH_CONTROL 0xAD +#define WM8350_CURRENT_SINK_DRIVER_B 0xAE +#define WM8350_CSB_FLASH_CONTROL 0xAF +#define WM8350_DCDC_LDO_REQUESTED 0xB0 +#define WM8350_DCDC_ACTIVE_OPTIONS 0xB1 +#define WM8350_DCDC_SLEEP_OPTIONS 0xB2 +#define WM8350_POWER_CHECK_COMPARATOR 0xB3 +#define WM8350_DCDC1_CONTROL 0xB4 +#define WM8350_DCDC1_TIMEOUTS 0xB5 +#define WM8350_DCDC1_LOW_POWER 0xB6 +#define WM8350_DCDC2_CONTROL 0xB7 +#define WM8350_DCDC2_TIMEOUTS 0xB8 +#define WM8350_DCDC3_CONTROL 0xBA +#define WM8350_DCDC3_TIMEOUTS 0xBB +#define WM8350_DCDC3_LOW_POWER 0xBC +#define WM8350_DCDC4_CONTROL 0xBD +#define WM8350_DCDC4_TIMEOUTS 0xBE +#define WM8350_DCDC4_LOW_POWER 0xBF +#define WM8350_DCDC5_CONTROL 0xC0 +#define WM8350_DCDC5_TIMEOUTS 0xC1 +#define WM8350_DCDC6_CONTROL 0xC3 +#define WM8350_DCDC6_TIMEOUTS 0xC4 +#define WM8350_DCDC6_LOW_POWER 0xC5 +#define WM8350_LIMIT_SWITCH_CONTROL 0xC7 +#define WM8350_LDO1_CONTROL 0xC8 +#define WM8350_LDO1_TIMEOUTS 0xC9 +#define WM8350_LDO1_LOW_POWER 0xCA +#define WM8350_LDO2_CONTROL 0xCB +#define WM8350_LDO2_TIMEOUTS 0xCC +#define WM8350_LDO2_LOW_POWER 0xCD +#define WM8350_LDO3_CONTROL 0xCE +#define WM8350_LDO3_TIMEOUTS 0xCF +#define WM8350_LDO3_LOW_POWER 0xD0 +#define WM8350_LDO4_CONTROL 0xD1 +#define WM8350_LDO4_TIMEOUTS 0xD2 +#define WM8350_LDO4_LOW_POWER 0xD3 +#define WM8350_VCC_FAULT_MASKS 0xD7 +#define WM8350_MAIN_BANDGAP_CONTROL 0xD8 +#define WM8350_OSC_CONTROL 0xD9 +#define WM8350_RTC_TICK_CONTROL 0xDA +#define WM8350_SECURITY 0xDB +#define WM8350_RAM_BIST_1 0xDC +#define WM8350_DCDC_LDO_STATUS 0xE1 +#define WM8350_GPIO_PIN_STATUS 0xE6 + +#define WM8350_DCDC1_FORCE_PWM 0xF8 +#define WM8350_DCDC3_FORCE_PWM 0xFA +#define WM8350_DCDC4_FORCE_PWM 0xFB +#define WM8350_DCDC6_FORCE_PWM 0xFD + +/* + * R172 (0xAC) - Current Sink Driver A + */ +#define WM8350_CS1_HIB_MODE 0x1000 +#define WM8350_CS1_HIB_MODE_MASK 0x1000 +#define WM8350_CS1_HIB_MODE_SHIFT 12 +#define WM8350_CS1_ISEL_MASK 0x003F +#define WM8350_CS1_ISEL_SHIFT 0 + +/* Bit values for R172 (0xAC) */ +#define WM8350_CS1_HIB_MODE_DISABLE 0 +#define WM8350_CS1_HIB_MODE_LEAVE 1 + +#define WM8350_CS1_ISEL_220M 0x3F + +/* + * R173 (0xAD) - CSA Flash control + */ +#define WM8350_CS1_FLASH_MODE 0x8000 +#define WM8350_CS1_TRIGSRC 0x4000 +#define WM8350_CS1_DRIVE 0x2000 +#define WM8350_CS1_FLASH_DUR_MASK 0x0300 +#define WM8350_CS1_OFF_RAMP_MASK 0x0030 +#define WM8350_CS1_ON_RAMP_MASK 0x0003 + +/* + * R174 (0xAE) - Current Sink Driver B + */ +#define WM8350_CS2_HIB_MODE 0x1000 +#define WM8350_CS2_ISEL_MASK 0x003F + +/* + * R175 (0xAF) - CSB Flash control + */ +#define WM8350_CS2_FLASH_MODE 0x8000 +#define WM8350_CS2_TRIGSRC 0x4000 +#define WM8350_CS2_DRIVE 0x2000 +#define WM8350_CS2_FLASH_DUR_MASK 0x0300 +#define WM8350_CS2_OFF_RAMP_MASK 0x0030 +#define WM8350_CS2_ON_RAMP_MASK 0x0003 + +/* + * R176 (0xB0) - DCDC/LDO requested + */ +#define WM8350_LS_ENA 0x8000 +#define WM8350_LDO4_ENA 0x0800 +#define WM8350_LDO3_ENA 0x0400 +#define WM8350_LDO2_ENA 0x0200 +#define WM8350_LDO1_ENA 0x0100 +#define WM8350_DC6_ENA 0x0020 +#define WM8350_DC5_ENA 0x0010 +#define WM8350_DC4_ENA 0x0008 +#define WM8350_DC3_ENA 0x0004 +#define WM8350_DC2_ENA 0x0002 +#define WM8350_DC1_ENA 0x0001 + +/* + * R177 (0xB1) - DCDC Active options + */ +#define WM8350_PUTO_MASK 0x3000 +#define WM8350_PWRUP_DELAY_MASK 0x0300 +#define WM8350_DC6_ACTIVE 0x0020 +#define WM8350_DC4_ACTIVE 0x0008 +#define WM8350_DC3_ACTIVE 0x0004 +#define WM8350_DC1_ACTIVE 0x0001 + +/* + * R178 (0xB2) - DCDC Sleep options + */ +#define WM8350_DC6_SLEEP 0x0020 +#define WM8350_DC4_SLEEP 0x0008 +#define WM8350_DC3_SLEEP 0x0004 +#define WM8350_DC1_SLEEP 0x0001 + +/* + * R179 (0xB3) - Power-check comparator + */ +#define WM8350_PCCMP_ERRACT 0x4000 +#define WM8350_PCCMP_RAIL 0x0100 +#define WM8350_PCCMP_OFF_THR_MASK 0x0070 +#define WM8350_PCCMP_ON_THR_MASK 0x0007 + +/* + * R180 (0xB4) - DCDC1 Control + */ +#define WM8350_DC1_OPFLT 0x0400 +#define WM8350_DC1_VSEL_MASK 0x007F +#define WM8350_DC1_VSEL_SHIFT 0 + +/* + * R181 (0xB5) - DCDC1 Timeouts + */ +#define WM8350_DC1_ERRACT_MASK 0xC000 +#define WM8350_DC1_ERRACT_SHIFT 14 +#define WM8350_DC1_ENSLOT_MASK 0x3C00 +#define WM8350_DC1_ENSLOT_SHIFT 10 +#define WM8350_DC1_SDSLOT_MASK 0x03C0 +#define WM8350_DC1_UVTO_MASK 0x0030 +#define WM8350_DC1_SDSLOT_SHIFT 6 + +/* Bit values for R181 (0xB5) */ +#define WM8350_DC1_ERRACT_NONE 0 +#define WM8350_DC1_ERRACT_SHUTDOWN_CONV 1 +#define WM8350_DC1_ERRACT_SHUTDOWN_SYS 2 + +/* + * R182 (0xB6) - DCDC1 Low Power + */ +#define WM8350_DC1_HIB_MODE_MASK 0x7000 +#define WM8350_DC1_HIB_TRIG_MASK 0x0300 +#define WM8350_DC1_VIMG_MASK 0x007F + +/* + * R183 (0xB7) - DCDC2 Control + */ +#define WM8350_DC2_MODE 0x4000 +#define WM8350_DC2_MODE_MASK 0x4000 +#define WM8350_DC2_MODE_SHIFT 14 +#define WM8350_DC2_HIB_MODE 0x1000 +#define WM8350_DC2_HIB_MODE_MASK 0x1000 +#define WM8350_DC2_HIB_MODE_SHIFT 12 +#define WM8350_DC2_HIB_TRIG_MASK 0x0300 +#define WM8350_DC2_HIB_TRIG_SHIFT 8 +#define WM8350_DC2_ILIM 0x0040 +#define WM8350_DC2_ILIM_MASK 0x0040 +#define WM8350_DC2_ILIM_SHIFT 6 +#define WM8350_DC2_RMP_MASK 0x0018 +#define WM8350_DC2_RMP_SHIFT 3 +#define WM8350_DC2_FBSRC_MASK 0x0003 +#define WM8350_DC2_FBSRC_SHIFT 0 + +/* Bit values for R183 (0xB7) */ +#define WM8350_DC2_MODE_BOOST 0 +#define WM8350_DC2_MODE_SWITCH 1 + +#define WM8350_DC2_HIB_MODE_ACTIVE 1 +#define WM8350_DC2_HIB_MODE_DISABLE 0 + +#define WM8350_DC2_HIB_TRIG_NONE 0 +#define WM8350_DC2_HIB_TRIG_LPWR1 1 +#define WM8350_DC2_HIB_TRIG_LPWR2 2 +#define WM8350_DC2_HIB_TRIG_LPWR3 3 + +#define WM8350_DC2_ILIM_HIGH 0 +#define WM8350_DC2_ILIM_LOW 1 + +#define WM8350_DC2_RMP_30V 0 +#define WM8350_DC2_RMP_20V 1 +#define WM8350_DC2_RMP_10V 2 +#define WM8350_DC2_RMP_5V 3 + +#define WM8350_DC2_FBSRC_FB2 0 +#define WM8350_DC2_FBSRC_ISINKA 1 +#define WM8350_DC2_FBSRC_ISINKB 2 +#define WM8350_DC2_FBSRC_USB 3 + +/* + * R184 (0xB8) - DCDC2 Timeouts + */ +#define WM8350_DC2_ERRACT_MASK 0xC000 +#define WM8350_DC2_ERRACT_SHIFT 14 +#define WM8350_DC2_ENSLOT_MASK 0x3C00 +#define WM8350_DC2_ENSLOT_SHIFT 10 +#define WM8350_DC2_SDSLOT_MASK 0x03C0 +#define WM8350_DC2_UVTO_MASK 0x0030 + +/* Bit values for R184 (0xB8) */ +#define WM8350_DC2_ERRACT_NONE 0 +#define WM8350_DC2_ERRACT_SHUTDOWN_CONV 1 +#define WM8350_DC2_ERRACT_SHUTDOWN_SYS 2 + +/* + * R186 (0xBA) - DCDC3 Control + */ +#define WM8350_DC3_OPFLT 0x0400 +#define WM8350_DC3_VSEL_MASK 0x007F +#define WM8350_DC3_VSEL_SHIFT 0 + +/* + * R187 (0xBB) - DCDC3 Timeouts + */ +#define WM8350_DC3_ERRACT_MASK 0xC000 +#define WM8350_DC3_ERRACT_SHIFT 14 +#define WM8350_DC3_ENSLOT_MASK 0x3C00 +#define WM8350_DC3_ENSLOT_SHIFT 10 +#define WM8350_DC3_SDSLOT_MASK 0x03C0 +#define WM8350_DC3_UVTO_MASK 0x0030 +#define WM8350_DC3_SDSLOT_SHIFT 6 + +/* Bit values for R187 (0xBB) */ +#define WM8350_DC3_ERRACT_NONE 0 +#define WM8350_DC3_ERRACT_SHUTDOWN_CONV 1 +#define WM8350_DC3_ERRACT_SHUTDOWN_SYS 2 +/* + * R188 (0xBC) - DCDC3 Low Power + */ +#define WM8350_DC3_HIB_MODE_MASK 0x7000 +#define WM8350_DC3_HIB_TRIG_MASK 0x0300 +#define WM8350_DC3_VIMG_MASK 0x007F + +/* + * R189 (0xBD) - DCDC4 Control + */ +#define WM8350_DC4_OPFLT 0x0400 +#define WM8350_DC4_VSEL_MASK 0x007F +#define WM8350_DC4_VSEL_SHIFT 0 + +/* + * R190 (0xBE) - DCDC4 Timeouts + */ +#define WM8350_DC4_ERRACT_MASK 0xC000 +#define WM8350_DC4_ERRACT_SHIFT 14 +#define WM8350_DC4_ENSLOT_MASK 0x3C00 +#define WM8350_DC4_ENSLOT_SHIFT 10 +#define WM8350_DC4_SDSLOT_MASK 0x03C0 +#define WM8350_DC4_UVTO_MASK 0x0030 +#define WM8350_DC4_SDSLOT_SHIFT 6 + +/* Bit values for R190 (0xBE) */ +#define WM8350_DC4_ERRACT_NONE 0 +#define WM8350_DC4_ERRACT_SHUTDOWN_CONV 1 +#define WM8350_DC4_ERRACT_SHUTDOWN_SYS 2 + +/* + * R191 (0xBF) - DCDC4 Low Power + */ +#define WM8350_DC4_HIB_MODE_MASK 0x7000 +#define WM8350_DC4_HIB_TRIG_MASK 0x0300 +#define WM8350_DC4_VIMG_MASK 0x007F + +/* + * R192 (0xC0) - DCDC5 Control + */ +#define WM8350_DC5_MODE 0x4000 +#define WM8350_DC5_MODE_MASK 0x4000 +#define WM8350_DC5_MODE_SHIFT 14 +#define WM8350_DC5_HIB_MODE 0x1000 +#define WM8350_DC5_HIB_MODE_MASK 0x1000 +#define WM8350_DC5_HIB_MODE_SHIFT 12 +#define WM8350_DC5_HIB_TRIG_MASK 0x0300 +#define WM8350_DC5_HIB_TRIG_SHIFT 8 +#define WM8350_DC5_ILIM 0x0040 +#define WM8350_DC5_ILIM_MASK 0x0040 +#define WM8350_DC5_ILIM_SHIFT 6 +#define WM8350_DC5_RMP_MASK 0x0018 +#define WM8350_DC5_RMP_SHIFT 3 +#define WM8350_DC5_FBSRC_MASK 0x0003 +#define WM8350_DC5_FBSRC_SHIFT 0 + +/* Bit values for R192 (0xC0) */ +#define WM8350_DC5_MODE_BOOST 0 +#define WM8350_DC5_MODE_SWITCH 1 + +#define WM8350_DC5_HIB_MODE_ACTIVE 1 +#define WM8350_DC5_HIB_MODE_DISABLE 0 + +#define WM8350_DC5_HIB_TRIG_NONE 0 +#define WM8350_DC5_HIB_TRIG_LPWR1 1 +#define WM8350_DC5_HIB_TRIG_LPWR2 2 +#define WM8350_DC5_HIB_TRIG_LPWR3 3 + +#define WM8350_DC5_ILIM_HIGH 0 +#define WM8350_DC5_ILIM_LOW 1 + +#define WM8350_DC5_RMP_30V 0 +#define WM8350_DC5_RMP_20V 1 +#define WM8350_DC5_RMP_10V 2 +#define WM8350_DC5_RMP_5V 3 + +#define WM8350_DC5_FBSRC_FB2 0 +#define WM8350_DC5_FBSRC_ISINKA 1 +#define WM8350_DC5_FBSRC_ISINKB 2 +#define WM8350_DC5_FBSRC_USB 3 + +/* + * R193 (0xC1) - DCDC5 Timeouts + */ +#define WM8350_DC5_ERRACT_MASK 0xC000 +#define WM8350_DC5_ERRACT_SHIFT 14 +#define WM8350_DC5_ENSLOT_MASK 0x3C00 +#define WM8350_DC5_ENSLOT_SHIFT 10 +#define WM8350_DC5_SDSLOT_MASK 0x03C0 +#define WM8350_DC5_UVTO_MASK 0x0030 +#define WM8350_DC5_SDSLOT_SHIFT 6 + +/* Bit values for R193 (0xC1) */ +#define WM8350_DC5_ERRACT_NONE 0 +#define WM8350_DC5_ERRACT_SHUTDOWN_CONV 1 +#define WM8350_DC5_ERRACT_SHUTDOWN_SYS 2 + +/* + * R195 (0xC3) - DCDC6 Control + */ +#define WM8350_DC6_OPFLT 0x0400 +#define WM8350_DC6_VSEL_MASK 0x007F +#define WM8350_DC6_VSEL_SHIFT 0 + +/* + * R196 (0xC4) - DCDC6 Timeouts + */ +#define WM8350_DC6_ERRACT_MASK 0xC000 +#define WM8350_DC6_ERRACT_SHIFT 14 +#define WM8350_DC6_ENSLOT_MASK 0x3C00 +#define WM8350_DC6_ENSLOT_SHIFT 10 +#define WM8350_DC6_SDSLOT_MASK 0x03C0 +#define WM8350_DC6_UVTO_MASK 0x0030 +#define WM8350_DC6_SDSLOT_SHIFT 6 + +/* Bit values for R196 (0xC4) */ +#define WM8350_DC6_ERRACT_NONE 0 +#define WM8350_DC6_ERRACT_SHUTDOWN_CONV 1 +#define WM8350_DC6_ERRACT_SHUTDOWN_SYS 2 + +/* + * R197 (0xC5) - DCDC6 Low Power + */ +#define WM8350_DC6_HIB_MODE_MASK 0x7000 +#define WM8350_DC6_HIB_TRIG_MASK 0x0300 +#define WM8350_DC6_VIMG_MASK 0x007F + +/* + * R199 (0xC7) - Limit Switch Control + */ +#define WM8350_LS_ERRACT_MASK 0xC000 +#define WM8350_LS_ERRACT_SHIFT 14 +#define WM8350_LS_ENSLOT_MASK 0x3C00 +#define WM8350_LS_ENSLOT_SHIFT 10 +#define WM8350_LS_SDSLOT_MASK 0x03C0 +#define WM8350_LS_SDSLOT_SHIFT 6 +#define WM8350_LS_HIB_MODE 0x0010 +#define WM8350_LS_HIB_MODE_MASK 0x0010 +#define WM8350_LS_HIB_MODE_SHIFT 4 +#define WM8350_LS_HIB_PROT 0x0002 +#define WM8350_LS_HIB_PROT_MASK 0x0002 +#define WM8350_LS_HIB_PROT_SHIFT 1 +#define WM8350_LS_PROT 0x0001 +#define WM8350_LS_PROT_MASK 0x0001 +#define WM8350_LS_PROT_SHIFT 0 + +/* Bit values for R199 (0xC7) */ +#define WM8350_LS_ERRACT_NONE 0 +#define WM8350_LS_ERRACT_SHUTDOWN_CONV 1 +#define WM8350_LS_ERRACT_SHUTDOWN_SYS 2 + +/* + * R200 (0xC8) - LDO1 Control + */ +#define WM8350_LDO1_SWI 0x4000 +#define WM8350_LDO1_OPFLT 0x0400 +#define WM8350_LDO1_VSEL_MASK 0x001F +#define WM8350_LDO1_VSEL_SHIFT 0 + +/* + * R201 (0xC9) - LDO1 Timeouts + */ +#define WM8350_LDO1_ERRACT_MASK 0xC000 +#define WM8350_LDO1_ERRACT_SHIFT 14 +#define WM8350_LDO1_ENSLOT_MASK 0x3C00 +#define WM8350_LDO1_ENSLOT_SHIFT 10 +#define WM8350_LDO1_SDSLOT_MASK 0x03C0 +#define WM8350_LDO1_UVTO_MASK 0x0030 +#define WM8350_LDO1_SDSLOT_SHIFT 6 + +/* Bit values for R201 (0xC9) */ +#define WM8350_LDO1_ERRACT_NONE 0 +#define WM8350_LDO1_ERRACT_SHUTDOWN_CONV 1 +#define WM8350_LDO1_ERRACT_SHUTDOWN_SYS 2 + +/* + * R202 (0xCA) - LDO1 Low Power + */ +#define WM8350_LDO1_HIB_MODE_MASK 0x3000 +#define WM8350_LDO1_HIB_TRIG_MASK 0x0300 +#define WM8350_LDO1_VIMG_MASK 0x001F +#define WM8350_LDO1_HIB_MODE_DIS (0x1 << 12) + + +/* + * R203 (0xCB) - LDO2 Control + */ +#define WM8350_LDO2_SWI 0x4000 +#define WM8350_LDO2_OPFLT 0x0400 +#define WM8350_LDO2_VSEL_MASK 0x001F +#define WM8350_LDO2_VSEL_SHIFT 0 + +/* + * R204 (0xCC) - LDO2 Timeouts + */ +#define WM8350_LDO2_ERRACT_MASK 0xC000 +#define WM8350_LDO2_ERRACT_SHIFT 14 +#define WM8350_LDO2_ENSLOT_MASK 0x3C00 +#define WM8350_LDO2_ENSLOT_SHIFT 10 +#define WM8350_LDO2_SDSLOT_MASK 0x03C0 +#define WM8350_LDO2_SDSLOT_SHIFT 6 + +/* Bit values for R204 (0xCC) */ +#define WM8350_LDO2_ERRACT_NONE 0 +#define WM8350_LDO2_ERRACT_SHUTDOWN_CONV 1 +#define WM8350_LDO2_ERRACT_SHUTDOWN_SYS 2 + +/* + * R205 (0xCD) - LDO2 Low Power + */ +#define WM8350_LDO2_HIB_MODE_MASK 0x3000 +#define WM8350_LDO2_HIB_TRIG_MASK 0x0300 +#define WM8350_LDO2_VIMG_MASK 0x001F + +/* + * R206 (0xCE) - LDO3 Control + */ +#define WM8350_LDO3_SWI 0x4000 +#define WM8350_LDO3_OPFLT 0x0400 +#define WM8350_LDO3_VSEL_MASK 0x001F +#define WM8350_LDO3_VSEL_SHIFT 0 + +/* + * R207 (0xCF) - LDO3 Timeouts + */ +#define WM8350_LDO3_ERRACT_MASK 0xC000 +#define WM8350_LDO3_ERRACT_SHIFT 14 +#define WM8350_LDO3_ENSLOT_MASK 0x3C00 +#define WM8350_LDO3_ENSLOT_SHIFT 10 +#define WM8350_LDO3_SDSLOT_MASK 0x03C0 +#define WM8350_LDO3_UVTO_MASK 0x0030 +#define WM8350_LDO3_SDSLOT_SHIFT 6 + +/* Bit values for R207 (0xCF) */ +#define WM8350_LDO3_ERRACT_NONE 0 +#define WM8350_LDO3_ERRACT_SHUTDOWN_CONV 1 +#define WM8350_LDO3_ERRACT_SHUTDOWN_SYS 2 + +/* + * R208 (0xD0) - LDO3 Low Power + */ +#define WM8350_LDO3_HIB_MODE_MASK 0x3000 +#define WM8350_LDO3_HIB_TRIG_MASK 0x0300 +#define WM8350_LDO3_VIMG_MASK 0x001F + +/* + * R209 (0xD1) - LDO4 Control + */ +#define WM8350_LDO4_SWI 0x4000 +#define WM8350_LDO4_OPFLT 0x0400 +#define WM8350_LDO4_VSEL_MASK 0x001F +#define WM8350_LDO4_VSEL_SHIFT 0 + +/* + * R210 (0xD2) - LDO4 Timeouts + */ +#define WM8350_LDO4_ERRACT_MASK 0xC000 +#define WM8350_LDO4_ERRACT_SHIFT 14 +#define WM8350_LDO4_ENSLOT_MASK 0x3C00 +#define WM8350_LDO4_ENSLOT_SHIFT 10 +#define WM8350_LDO4_SDSLOT_MASK 0x03C0 +#define WM8350_LDO4_UVTO_MASK 0x0030 +#define WM8350_LDO4_SDSLOT_SHIFT 6 + +/* Bit values for R210 (0xD2) */ +#define WM8350_LDO4_ERRACT_NONE 0 +#define WM8350_LDO4_ERRACT_SHUTDOWN_CONV 1 +#define WM8350_LDO4_ERRACT_SHUTDOWN_SYS 2 + +/* + * R211 (0xD3) - LDO4 Low Power + */ +#define WM8350_LDO4_HIB_MODE_MASK 0x3000 +#define WM8350_LDO4_HIB_TRIG_MASK 0x0300 +#define WM8350_LDO4_VIMG_MASK 0x001F + +/* + * R215 (0xD7) - VCC_FAULT Masks + */ +#define WM8350_LS_FAULT 0x8000 +#define WM8350_LDO4_FAULT 0x0800 +#define WM8350_LDO3_FAULT 0x0400 +#define WM8350_LDO2_FAULT 0x0200 +#define WM8350_LDO1_FAULT 0x0100 +#define WM8350_DC6_FAULT 0x0020 +#define WM8350_DC5_FAULT 0x0010 +#define WM8350_DC4_FAULT 0x0008 +#define WM8350_DC3_FAULT 0x0004 +#define WM8350_DC2_FAULT 0x0002 +#define WM8350_DC1_FAULT 0x0001 + +/* + * R216 (0xD8) - Main Bandgap Control + */ +#define WM8350_MBG_LOAD_FUSES 0x8000 +#define WM8350_MBG_FUSE_WPREP 0x4000 +#define WM8350_MBG_FUSE_WRITE 0x2000 +#define WM8350_MBG_FUSE_TRIM_MASK 0x1F00 +#define WM8350_MBG_TRIM_SRC 0x0020 +#define WM8350_MBG_USER_TRIM_MASK 0x001F + +/* + * R217 (0xD9) - OSC Control + */ +#define WM8350_OSC_LOAD_FUSES 0x8000 +#define WM8350_OSC_FUSE_WPREP 0x4000 +#define WM8350_OSC_FUSE_WRITE 0x2000 +#define WM8350_OSC_FUSE_TRIM_MASK 0x0F00 +#define WM8350_OSC_TRIM_SRC 0x0020 +#define WM8350_OSC_USER_TRIM_MASK 0x000F + +/* + * R248 (0xF8) - DCDC1 Force PWM + */ +#define WM8350_DCDC1_FORCE_PWM_ENA 0x0010 + +/* + * R250 (0xFA) - DCDC3 Force PWM + */ +#define WM8350_DCDC3_FORCE_PWM_ENA 0x0010 + +/* + * R251 (0xFB) - DCDC4 Force PWM + */ +#define WM8350_DCDC4_FORCE_PWM_ENA 0x0010 + +/* + * R253 (0xFD) - DCDC1 Force PWM + */ +#define WM8350_DCDC6_FORCE_PWM_ENA 0x0010 + +/* + * DCDC's + */ +#define WM8350_DCDC_1 0 +#define WM8350_DCDC_2 1 +#define WM8350_DCDC_3 2 +#define WM8350_DCDC_4 3 +#define WM8350_DCDC_5 4 +#define WM8350_DCDC_6 5 + +/* DCDC modes */ +#define WM8350_DCDC_ACTIVE_STANDBY 0 +#define WM8350_DCDC_ACTIVE_PULSE 1 +#define WM8350_DCDC_SLEEP_NORMAL 0 +#define WM8350_DCDC_SLEEP_LOW 1 + +/* DCDC Low power (Hibernate) mode */ +#define WM8350_DCDC_HIB_MODE_CUR (0 << 12) +#define WM8350_DCDC_HIB_MODE_IMAGE (1 << 12) +#define WM8350_DCDC_HIB_MODE_STANDBY (2 << 12) +#define WM8350_DCDC_HIB_MODE_LDO (4 << 12) +#define WM8350_DCDC_HIB_MODE_LDO_IM (5 << 12) +#define WM8350_DCDC_HIB_MODE_DIS (7 << 12) +#define WM8350_DCDC_HIB_MODE_MASK (7 << 12) + +/* DCDC Low Power (Hibernate) signal */ +#define WM8350_DCDC_HIB_SIG_REG (0 << 8) +#define WM8350_DCDC_HIB_SIG_LPWR1 (1 << 8) +#define WM8350_DCDC_HIB_SIG_LPWR2 (2 << 8) +#define WM8350_DCDC_HIB_SIG_LPWR3 (3 << 8) + +/* LDO Low power (Hibernate) mode */ +#define WM8350_LDO_HIB_MODE_IMAGE (0 << 0) +#define WM8350_LDO_HIB_MODE_DIS (1 << 0) + +/* LDO Low Power (Hibernate) signal */ +#define WM8350_LDO_HIB_SIG_REG (0 << 8) +#define WM8350_LDO_HIB_SIG_LPWR1 (1 << 8) +#define WM8350_LDO_HIB_SIG_LPWR2 (2 << 8) +#define WM8350_LDO_HIB_SIG_LPWR3 (3 << 8) + +/* + * LDOs + */ +#define WM8350_LDO_1 6 +#define WM8350_LDO_2 7 +#define WM8350_LDO_3 8 +#define WM8350_LDO_4 9 + +/* + * ISINKs + */ +#define WM8350_ISINK_A 10 +#define WM8350_ISINK_B 11 + +#define WM8350_ISINK_MODE_BOOST 0 +#define WM8350_ISINK_MODE_SWITCH 1 +#define WM8350_ISINK_ILIM_NORMAL 0 +#define WM8350_ISINK_ILIM_LOW 1 + +#define WM8350_ISINK_FLASH_DISABLE 0 +#define WM8350_ISINK_FLASH_ENABLE 1 +#define WM8350_ISINK_FLASH_TRIG_BIT 0 +#define WM8350_ISINK_FLASH_TRIG_GPIO 1 +#define WM8350_ISINK_FLASH_MODE_EN (1 << 13) +#define WM8350_ISINK_FLASH_MODE_DIS (0 << 13) +#define WM8350_ISINK_FLASH_DUR_32MS (0 << 8) +#define WM8350_ISINK_FLASH_DUR_64MS (1 << 8) +#define WM8350_ISINK_FLASH_DUR_96MS (2 << 8) +#define WM8350_ISINK_FLASH_DUR_1024MS (3 << 8) +#define WM8350_ISINK_FLASH_ON_INSTANT (0 << 4) +#define WM8350_ISINK_FLASH_ON_0_25S (1 << 4) +#define WM8350_ISINK_FLASH_ON_0_50S (2 << 4) +#define WM8350_ISINK_FLASH_ON_1_00S (3 << 4) +#define WM8350_ISINK_FLASH_ON_1_95S (1 << 4) +#define WM8350_ISINK_FLASH_ON_3_91S (2 << 4) +#define WM8350_ISINK_FLASH_ON_7_80S (3 << 4) +#define WM8350_ISINK_FLASH_OFF_INSTANT (0 << 0) +#define WM8350_ISINK_FLASH_OFF_0_25S (1 << 0) +#define WM8350_ISINK_FLASH_OFF_0_50S (2 << 0) +#define WM8350_ISINK_FLASH_OFF_1_00S (3 << 0) +#define WM8350_ISINK_FLASH_OFF_1_95S (1 << 0) +#define WM8350_ISINK_FLASH_OFF_3_91S (2 << 0) +#define WM8350_ISINK_FLASH_OFF_7_80S (3 << 0) + +/* + * Regulator Interrupts. + */ +#define WM8350_IRQ_CS1 13 +#define WM8350_IRQ_CS2 14 +#define WM8350_IRQ_UV_LDO4 25 +#define WM8350_IRQ_UV_LDO3 26 +#define WM8350_IRQ_UV_LDO2 27 +#define WM8350_IRQ_UV_LDO1 28 +#define WM8350_IRQ_UV_DC6 29 +#define WM8350_IRQ_UV_DC5 30 +#define WM8350_IRQ_UV_DC4 31 +#define WM8350_IRQ_UV_DC3 32 +#define WM8350_IRQ_UV_DC2 33 +#define WM8350_IRQ_UV_DC1 34 +#define WM8350_IRQ_OC_LS 35 + +#define NUM_WM8350_REGULATORS 12 + +struct wm8350; +struct platform_device; +struct regulator_init_data; + +struct wm8350_pmic { + /* ISINK to DCDC mapping */ + int isink_A_dcdc; + int isink_B_dcdc; + + /* hibernate configs */ + u16 dcdc1_hib_mode; + u16 dcdc3_hib_mode; + u16 dcdc4_hib_mode; + u16 dcdc6_hib_mode; + + /* regulator devices */ + struct platform_device *pdev[NUM_WM8350_REGULATORS]; +}; + +int wm8350_register_regulator(struct wm8350 *wm8350, int reg, + struct regulator_init_data *initdata); + +/* + * Additional DCDC control not supported via regulator API + */ +int wm8350_dcdc_set_slot(struct wm8350 *wm8350, int dcdc, u16 start, + u16 stop, u16 fault); +int wm8350_dcdc25_set_mode(struct wm8350 *wm8350, int dcdc, u16 mode, + u16 ilim, u16 ramp, u16 feedback); + +/* + * Additional LDO control not supported via regulator API + */ +int wm8350_ldo_set_slot(struct wm8350 *wm8350, int ldo, u16 start, u16 stop); + +/* + * Additional ISINK control not supported via regulator API + */ +int wm8350_isink_set_flash(struct wm8350 *wm8350, int isink, u16 mode, + u16 trigger, u16 duration, u16 on_ramp, + u16 off_ramp, u16 drive); + +#endif diff --git a/include/linux/mfd/wm8350/rtc.h b/include/linux/mfd/wm8350/rtc.h new file mode 100644 index 000000000000..dfda69e9f440 --- /dev/null +++ b/include/linux/mfd/wm8350/rtc.h @@ -0,0 +1,266 @@ +/* + * rtc.h -- RTC driver for Wolfson WM8350 PMIC + * + * Copyright 2007 Wolfson Microelectronics PLC + * + * 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 __LINUX_MFD_WM8350_RTC_H +#define __LINUX_MFD_WM8350_RTC_H + +#include <linux/platform_device.h> + +/* + * Register values. + */ +#define WM8350_RTC_SECONDS_MINUTES 0x10 +#define WM8350_RTC_HOURS_DAY 0x11 +#define WM8350_RTC_DATE_MONTH 0x12 +#define WM8350_RTC_YEAR 0x13 +#define WM8350_ALARM_SECONDS_MINUTES 0x14 +#define WM8350_ALARM_HOURS_DAY 0x15 +#define WM8350_ALARM_DATE_MONTH 0x16 +#define WM8350_RTC_TIME_CONTROL 0x17 + +/* + * R16 (0x10) - RTC Seconds/Minutes + */ +#define WM8350_RTC_MINS_MASK 0x7F00 +#define WM8350_RTC_MINS_SHIFT 8 +#define WM8350_RTC_SECS_MASK 0x007F +#define WM8350_RTC_SECS_SHIFT 0 + +/* + * R17 (0x11) - RTC Hours/Day + */ +#define WM8350_RTC_DAY_MASK 0x0700 +#define WM8350_RTC_DAY_SHIFT 8 +#define WM8350_RTC_HPM_MASK 0x0020 +#define WM8350_RTC_HPM_SHIFT 5 +#define WM8350_RTC_HRS_MASK 0x001F +#define WM8350_RTC_HRS_SHIFT 0 + +/* Bit values for R21 (0x15) */ +#define WM8350_RTC_DAY_SUN 1 +#define WM8350_RTC_DAY_MON 2 +#define WM8350_RTC_DAY_TUE 3 +#define WM8350_RTC_DAY_WED 4 +#define WM8350_RTC_DAY_THU 5 +#define WM8350_RTC_DAY_FRI 6 +#define WM8350_RTC_DAY_SAT 7 + +#define WM8350_RTC_HPM_AM 0 +#define WM8350_RTC_HPM_PM 1 + +/* + * R18 (0x12) - RTC Date/Month + */ +#define WM8350_RTC_MTH_MASK 0x1F00 +#define WM8350_RTC_MTH_SHIFT 8 +#define WM8350_RTC_DATE_MASK 0x003F +#define WM8350_RTC_DATE_SHIFT 0 + +/* Bit values for R22 (0x16) */ +#define WM8350_RTC_MTH_JAN 1 +#define WM8350_RTC_MTH_FEB 2 +#define WM8350_RTC_MTH_MAR 3 +#define WM8350_RTC_MTH_APR 4 +#define WM8350_RTC_MTH_MAY 5 +#define WM8350_RTC_MTH_JUN 6 +#define WM8350_RTC_MTH_JUL 7 +#define WM8350_RTC_MTH_AUG 8 +#define WM8350_RTC_MTH_SEP 9 +#define WM8350_RTC_MTH_OCT 10 +#define WM8350_RTC_MTH_NOV 11 +#define WM8350_RTC_MTH_DEC 12 +#define WM8350_RTC_MTH_JAN_BCD 0x01 +#define WM8350_RTC_MTH_FEB_BCD 0x02 +#define WM8350_RTC_MTH_MAR_BCD 0x03 +#define WM8350_RTC_MTH_APR_BCD 0x04 +#define WM8350_RTC_MTH_MAY_BCD 0x05 +#define WM8350_RTC_MTH_JUN_BCD 0x06 +#define WM8350_RTC_MTH_JUL_BCD 0x07 +#define WM8350_RTC_MTH_AUG_BCD 0x08 +#define WM8350_RTC_MTH_SEP_BCD 0x09 +#define WM8350_RTC_MTH_OCT_BCD 0x10 +#define WM8350_RTC_MTH_NOV_BCD 0x11 +#define WM8350_RTC_MTH_DEC_BCD 0x12 + +/* + * R19 (0x13) - RTC Year + */ +#define WM8350_RTC_YHUNDREDS_MASK 0x3F00 +#define WM8350_RTC_YHUNDREDS_SHIFT 8 +#define WM8350_RTC_YUNITS_MASK 0x00FF +#define WM8350_RTC_YUNITS_SHIFT 0 + +/* + * R20 (0x14) - Alarm Seconds/Minutes + */ +#define WM8350_RTC_ALMMINS_MASK 0x7F00 +#define WM8350_RTC_ALMMINS_SHIFT 8 +#define WM8350_RTC_ALMSECS_MASK 0x007F +#define WM8350_RTC_ALMSECS_SHIFT 0 + +/* Bit values for R20 (0x14) */ +#define WM8350_RTC_ALMMINS_DONT_CARE -1 +#define WM8350_RTC_ALMSECS_DONT_CARE -1 + +/* + * R21 (0x15) - Alarm Hours/Day + */ +#define WM8350_RTC_ALMDAY_MASK 0x0F00 +#define WM8350_RTC_ALMDAY_SHIFT 8 +#define WM8350_RTC_ALMHPM_MASK 0x0020 +#define WM8350_RTC_ALMHPM_SHIFT 5 +#define WM8350_RTC_ALMHRS_MASK 0x001F +#define WM8350_RTC_ALMHRS_SHIFT 0 + +/* Bit values for R21 (0x15) */ +#define WM8350_RTC_ALMDAY_DONT_CARE -1 +#define WM8350_RTC_ALMDAY_SUN 1 +#define WM8350_RTC_ALMDAY_MON 2 +#define WM8350_RTC_ALMDAY_TUE 3 +#define WM8350_RTC_ALMDAY_WED 4 +#define WM8350_RTC_ALMDAY_THU 5 +#define WM8350_RTC_ALMDAY_FRI 6 +#define WM8350_RTC_ALMDAY_SAT 7 + +#define WM8350_RTC_ALMHPM_AM 0 +#define WM8350_RTC_ALMHPM_PM 1 + +#define WM8350_RTC_ALMHRS_DONT_CARE -1 + +/* + * R22 (0x16) - Alarm Date/Month + */ +#define WM8350_RTC_ALMMTH_MASK 0x1F00 +#define WM8350_RTC_ALMMTH_SHIFT 8 +#define WM8350_RTC_ALMDATE_MASK 0x003F +#define WM8350_RTC_ALMDATE_SHIFT 0 + +/* Bit values for R22 (0x16) */ +#define WM8350_RTC_ALMDATE_DONT_CARE -1 + +#define WM8350_RTC_ALMMTH_DONT_CARE -1 +#define WM8350_RTC_ALMMTH_JAN 1 +#define WM8350_RTC_ALMMTH_FEB 2 +#define WM8350_RTC_ALMMTH_MAR 3 +#define WM8350_RTC_ALMMTH_APR 4 +#define WM8350_RTC_ALMMTH_MAY 5 +#define WM8350_RTC_ALMMTH_JUN 6 +#define WM8350_RTC_ALMMTH_JUL 7 +#define WM8350_RTC_ALMMTH_AUG 8 +#define WM8350_RTC_ALMMTH_SEP 9 +#define WM8350_RTC_ALMMTH_OCT 10 +#define WM8350_RTC_ALMMTH_NOV 11 +#define WM8350_RTC_ALMMTH_DEC 12 +#define WM8350_RTC_ALMMTH_JAN_BCD 0x01 +#define WM8350_RTC_ALMMTH_FEB_BCD 0x02 +#define WM8350_RTC_ALMMTH_MAR_BCD 0x03 +#define WM8350_RTC_ALMMTH_APR_BCD 0x04 +#define WM8350_RTC_ALMMTH_MAY_BCD 0x05 +#define WM8350_RTC_ALMMTH_JUN_BCD 0x06 +#define WM8350_RTC_ALMMTH_JUL_BCD 0x07 +#define WM8350_RTC_ALMMTH_AUG_BCD 0x08 +#define WM8350_RTC_ALMMTH_SEP_BCD 0x09 +#define WM8350_RTC_ALMMTH_OCT_BCD 0x10 +#define WM8350_RTC_ALMMTH_NOV_BCD 0x11 +#define WM8350_RTC_ALMMTH_DEC_BCD 0x12 + +/* + * R23 (0x17) - RTC Time Control + */ +#define WM8350_RTC_BCD 0x8000 +#define WM8350_RTC_BCD_MASK 0x8000 +#define WM8350_RTC_BCD_SHIFT 15 +#define WM8350_RTC_12HR 0x4000 +#define WM8350_RTC_12HR_MASK 0x4000 +#define WM8350_RTC_12HR_SHIFT 14 +#define WM8350_RTC_DST 0x2000 +#define WM8350_RTC_DST_MASK 0x2000 +#define WM8350_RTC_DST_SHIFT 13 +#define WM8350_RTC_SET 0x0800 +#define WM8350_RTC_SET_MASK 0x0800 +#define WM8350_RTC_SET_SHIFT 11 +#define WM8350_RTC_STS 0x0400 +#define WM8350_RTC_STS_MASK 0x0400 +#define WM8350_RTC_STS_SHIFT 10 +#define WM8350_RTC_ALMSET 0x0200 +#define WM8350_RTC_ALMSET_MASK 0x0200 +#define WM8350_RTC_ALMSET_SHIFT 9 +#define WM8350_RTC_ALMSTS 0x0100 +#define WM8350_RTC_ALMSTS_MASK 0x0100 +#define WM8350_RTC_ALMSTS_SHIFT 8 +#define WM8350_RTC_PINT 0x0070 +#define WM8350_RTC_PINT_MASK 0x0070 +#define WM8350_RTC_PINT_SHIFT 4 +#define WM8350_RTC_DSW 0x000F +#define WM8350_RTC_DSW_MASK 0x000F +#define WM8350_RTC_DSW_SHIFT 0 + +/* Bit values for R23 (0x17) */ +#define WM8350_RTC_BCD_BINARY 0 +#define WM8350_RTC_BCD_BCD 1 + +#define WM8350_RTC_12HR_24HR 0 +#define WM8350_RTC_12HR_12HR 1 + +#define WM8350_RTC_DST_DISABLED 0 +#define WM8350_RTC_DST_ENABLED 1 + +#define WM8350_RTC_SET_RUN 0 +#define WM8350_RTC_SET_SET 1 + +#define WM8350_RTC_STS_RUNNING 0 +#define WM8350_RTC_STS_STOPPED 1 + +#define WM8350_RTC_ALMSET_RUN 0 +#define WM8350_RTC_ALMSET_SET 1 + +#define WM8350_RTC_ALMSTS_RUNNING 0 +#define WM8350_RTC_ALMSTS_STOPPED 1 + +#define WM8350_RTC_PINT_DISABLED 0 +#define WM8350_RTC_PINT_SECS 1 +#define WM8350_RTC_PINT_MINS 2 +#define WM8350_RTC_PINT_HRS 3 +#define WM8350_RTC_PINT_DAYS 4 +#define WM8350_RTC_PINT_MTHS 5 + +#define WM8350_RTC_DSW_DISABLED 0 +#define WM8350_RTC_DSW_1HZ 1 +#define WM8350_RTC_DSW_2HZ 2 +#define WM8350_RTC_DSW_4HZ 3 +#define WM8350_RTC_DSW_8HZ 4 +#define WM8350_RTC_DSW_16HZ 5 +#define WM8350_RTC_DSW_32HZ 6 +#define WM8350_RTC_DSW_64HZ 7 +#define WM8350_RTC_DSW_128HZ 8 +#define WM8350_RTC_DSW_256HZ 9 +#define WM8350_RTC_DSW_512HZ 10 +#define WM8350_RTC_DSW_1024HZ 11 + +/* + * R218 (0xDA) - RTC Tick Control + */ +#define WM8350_RTC_TICKSTS 0x4000 +#define WM8350_RTC_CLKSRC 0x2000 +#define WM8350_RTC_TRIM_MASK 0x03FF + +/* + * RTC Interrupts. + */ +#define WM8350_IRQ_RTC_PER 7 +#define WM8350_IRQ_RTC_SEC 8 +#define WM8350_IRQ_RTC_ALM 9 + +struct wm8350_rtc { + struct platform_device *pdev; +}; + +#endif diff --git a/include/linux/mfd/wm8350/supply.h b/include/linux/mfd/wm8350/supply.h new file mode 100644 index 000000000000..1c8f3cde79b0 --- /dev/null +++ b/include/linux/mfd/wm8350/supply.h @@ -0,0 +1,111 @@ +/* + * supply.h -- Power Supply Driver for Wolfson WM8350 PMIC + * + * Copyright 2007 Wolfson Microelectronics PLC + * + * 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 __LINUX_MFD_WM8350_SUPPLY_H_ +#define __LINUX_MFD_WM8350_SUPPLY_H_ + +#include <linux/platform_device.h> + +/* + * Charger registers + */ +#define WM8350_BATTERY_CHARGER_CONTROL_1 0xA8 +#define WM8350_BATTERY_CHARGER_CONTROL_2 0xA9 +#define WM8350_BATTERY_CHARGER_CONTROL_3 0xAA + +/* + * R168 (0xA8) - Battery Charger Control 1 + */ +#define WM8350_CHG_ENA_R168 0x8000 +#define WM8350_CHG_THR 0x2000 +#define WM8350_CHG_EOC_SEL_MASK 0x1C00 +#define WM8350_CHG_TRICKLE_TEMP_CHOKE 0x0200 +#define WM8350_CHG_TRICKLE_USB_CHOKE 0x0100 +#define WM8350_CHG_RECOVER_T 0x0080 +#define WM8350_CHG_END_ACT 0x0040 +#define WM8350_CHG_FAST 0x0020 +#define WM8350_CHG_FAST_USB_THROTTLE 0x0010 +#define WM8350_CHG_NTC_MON 0x0008 +#define WM8350_CHG_BATT_HOT_MON 0x0004 +#define WM8350_CHG_BATT_COLD_MON 0x0002 +#define WM8350_CHG_CHIP_TEMP_MON 0x0001 + +/* + * R169 (0xA9) - Battery Charger Control 2 + */ +#define WM8350_CHG_ACTIVE 0x8000 +#define WM8350_CHG_PAUSE 0x4000 +#define WM8350_CHG_STS_MASK 0x3000 +#define WM8350_CHG_TIME_MASK 0x0F00 +#define WM8350_CHG_MASK_WALL_FB 0x0080 +#define WM8350_CHG_TRICKLE_SEL 0x0040 +#define WM8350_CHG_VSEL_MASK 0x0030 +#define WM8350_CHG_ISEL_MASK 0x000F +#define WM8350_CHG_STS_OFF 0x0000 +#define WM8350_CHG_STS_TRICKLE 0x1000 +#define WM8350_CHG_STS_FAST 0x2000 + +/* + * R170 (0xAA) - Battery Charger Control 3 + */ +#define WM8350_CHG_THROTTLE_T_MASK 0x0060 +#define WM8350_CHG_SMART 0x0010 +#define WM8350_CHG_TIMER_ADJT_MASK 0x000F + +/* + * Charger Interrupts + */ +#define WM8350_IRQ_CHG_BAT_HOT 0 +#define WM8350_IRQ_CHG_BAT_COLD 1 +#define WM8350_IRQ_CHG_BAT_FAIL 2 +#define WM8350_IRQ_CHG_TO 3 +#define WM8350_IRQ_CHG_END 4 +#define WM8350_IRQ_CHG_START 5 +#define WM8350_IRQ_CHG_FAST_RDY 6 +#define WM8350_IRQ_CHG_VBATT_LT_3P9 10 +#define WM8350_IRQ_CHG_VBATT_LT_3P1 11 +#define WM8350_IRQ_CHG_VBATT_LT_2P85 12 + +/* + * Charger Policy + */ +#define WM8350_CHG_TRICKLE_50mA (0 << 6) +#define WM8350_CHG_TRICKLE_100mA (1 << 6) +#define WM8350_CHG_4_05V (0 << 4) +#define WM8350_CHG_4_10V (1 << 4) +#define WM8350_CHG_4_15V (2 << 4) +#define WM8350_CHG_4_20V (3 << 4) +#define WM8350_CHG_FAST_LIMIT_mA(x) ((x / 50) & 0xf) +#define WM8350_CHG_EOC_mA(x) (((x - 10) & 0x7) << 10) +#define WM8350_CHG_TRICKLE_3_1V (0 << 13) +#define WM8350_CHG_TRICKLE_3_9V (1 << 13) + +/* + * Supply Registers. + */ +#define WM8350_USB_VOLTAGE_READBACK 0x9C +#define WM8350_LINE_VOLTAGE_READBACK 0x9D +#define WM8350_BATT_VOLTAGE_READBACK 0x9E + +/* + * Supply Interrupts. + */ +#define WM8350_IRQ_USB_LIMIT 15 +#define WM8350_IRQ_EXT_USB_FB 36 +#define WM8350_IRQ_EXT_WALL_FB 37 +#define WM8350_IRQ_EXT_BAT_FB 38 + +struct wm8350_power { + struct platform_device *pdev; +}; + +#endif diff --git a/include/linux/mfd/wm8350/wdt.h b/include/linux/mfd/wm8350/wdt.h new file mode 100644 index 000000000000..f6135b5e5ef4 --- /dev/null +++ b/include/linux/mfd/wm8350/wdt.h @@ -0,0 +1,28 @@ +/* + * wdt.h -- Watchdog Driver for Wolfson WM8350 PMIC + * + * Copyright 2007, 2008 Wolfson Microelectronics PLC + * + * 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 __LINUX_MFD_WM8350_WDT_H_ +#define __LINUX_MFD_WM8350_WDT_H_ + +#include <linux/platform_device.h> + +#define WM8350_WDOG_HIB_MODE 0x0080 +#define WM8350_WDOG_DEBUG 0x0040 +#define WM8350_WDOG_MODE_MASK 0x0030 +#define WM8350_WDOG_TO_MASK 0x0007 + +#define WM8350_IRQ_SYS_WDOG_TO 24 + +struct wm8350_wdt { + struct platform_device *pdev; +}; + +#endif diff --git a/include/linux/mfd/wm8400-audio.h b/include/linux/mfd/wm8400-audio.h new file mode 100644 index 000000000000..b6640e018046 --- /dev/null +++ b/include/linux/mfd/wm8400-audio.h @@ -0,0 +1,1186 @@ +/* + * wm8400 private definitions for audio + * + * Copyright 2008 Wolfson Microelectronics plc + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#ifndef __LINUX_MFD_WM8400_AUDIO_H +#define __LINUX_MFD_WM8400_AUDIO_H + +#include <linux/mfd/wm8400-audio.h> + +/* + * R2 (0x02) - Power Management (1) + */ +#define WM8400_CODEC_ENA 0x8000 /* CODEC_ENA */ +#define WM8400_CODEC_ENA_MASK 0x8000 /* CODEC_ENA */ +#define WM8400_CODEC_ENA_SHIFT 15 /* CODEC_ENA */ +#define WM8400_CODEC_ENA_WIDTH 1 /* CODEC_ENA */ +#define WM8400_SYSCLK_ENA 0x4000 /* SYSCLK_ENA */ +#define WM8400_SYSCLK_ENA_MASK 0x4000 /* SYSCLK_ENA */ +#define WM8400_SYSCLK_ENA_SHIFT 14 /* SYSCLK_ENA */ +#define WM8400_SYSCLK_ENA_WIDTH 1 /* SYSCLK_ENA */ +#define WM8400_SPK_MIX_ENA 0x2000 /* SPK_MIX_ENA */ +#define WM8400_SPK_MIX_ENA_MASK 0x2000 /* SPK_MIX_ENA */ +#define WM8400_SPK_MIX_ENA_SHIFT 13 /* SPK_MIX_ENA */ +#define WM8400_SPK_MIX_ENA_WIDTH 1 /* SPK_MIX_ENA */ +#define WM8400_SPK_ENA 0x1000 /* SPK_ENA */ +#define WM8400_SPK_ENA_MASK 0x1000 /* SPK_ENA */ +#define WM8400_SPK_ENA_SHIFT 12 /* SPK_ENA */ +#define WM8400_SPK_ENA_WIDTH 1 /* SPK_ENA */ +#define WM8400_OUT3_ENA 0x0800 /* OUT3_ENA */ +#define WM8400_OUT3_ENA_MASK 0x0800 /* OUT3_ENA */ +#define WM8400_OUT3_ENA_SHIFT 11 /* OUT3_ENA */ +#define WM8400_OUT3_ENA_WIDTH 1 /* OUT3_ENA */ +#define WM8400_OUT4_ENA 0x0400 /* OUT4_ENA */ +#define WM8400_OUT4_ENA_MASK 0x0400 /* OUT4_ENA */ +#define WM8400_OUT4_ENA_SHIFT 10 /* OUT4_ENA */ +#define WM8400_OUT4_ENA_WIDTH 1 /* OUT4_ENA */ +#define WM8400_LOUT_ENA 0x0200 /* LOUT_ENA */ +#define WM8400_LOUT_ENA_MASK 0x0200 /* LOUT_ENA */ +#define WM8400_LOUT_ENA_SHIFT 9 /* LOUT_ENA */ +#define WM8400_LOUT_ENA_WIDTH 1 /* LOUT_ENA */ +#define WM8400_ROUT_ENA 0x0100 /* ROUT_ENA */ +#define WM8400_ROUT_ENA_MASK 0x0100 /* ROUT_ENA */ +#define WM8400_ROUT_ENA_SHIFT 8 /* ROUT_ENA */ +#define WM8400_ROUT_ENA_WIDTH 1 /* ROUT_ENA */ +#define WM8400_MIC1BIAS_ENA 0x0010 /* MIC1BIAS_ENA */ +#define WM8400_MIC1BIAS_ENA_MASK 0x0010 /* MIC1BIAS_ENA */ +#define WM8400_MIC1BIAS_ENA_SHIFT 4 /* MIC1BIAS_ENA */ +#define WM8400_MIC1BIAS_ENA_WIDTH 1 /* MIC1BIAS_ENA */ +#define WM8400_VMID_MODE_MASK 0x0006 /* VMID_MODE - [2:1] */ +#define WM8400_VMID_MODE_SHIFT 1 /* VMID_MODE - [2:1] */ +#define WM8400_VMID_MODE_WIDTH 2 /* VMID_MODE - [2:1] */ +#define WM8400_VREF_ENA 0x0001 /* VREF_ENA */ +#define WM8400_VREF_ENA_MASK 0x0001 /* VREF_ENA */ +#define WM8400_VREF_ENA_SHIFT 0 /* VREF_ENA */ +#define WM8400_VREF_ENA_WIDTH 1 /* VREF_ENA */ + +/* + * R3 (0x03) - Power Management (2) + */ +#define WM8400_FLL_ENA 0x8000 /* FLL_ENA */ +#define WM8400_FLL_ENA_MASK 0x8000 /* FLL_ENA */ +#define WM8400_FLL_ENA_SHIFT 15 /* FLL_ENA */ +#define WM8400_FLL_ENA_WIDTH 1 /* FLL_ENA */ +#define WM8400_TSHUT_ENA 0x4000 /* TSHUT_ENA */ +#define WM8400_TSHUT_ENA_MASK 0x4000 /* TSHUT_ENA */ +#define WM8400_TSHUT_ENA_SHIFT 14 /* TSHUT_ENA */ +#define WM8400_TSHUT_ENA_WIDTH 1 /* TSHUT_ENA */ +#define WM8400_TSHUT_OPDIS 0x2000 /* TSHUT_OPDIS */ +#define WM8400_TSHUT_OPDIS_MASK 0x2000 /* TSHUT_OPDIS */ +#define WM8400_TSHUT_OPDIS_SHIFT 13 /* TSHUT_OPDIS */ +#define WM8400_TSHUT_OPDIS_WIDTH 1 /* TSHUT_OPDIS */ +#define WM8400_OPCLK_ENA 0x0800 /* OPCLK_ENA */ +#define WM8400_OPCLK_ENA_MASK 0x0800 /* OPCLK_ENA */ +#define WM8400_OPCLK_ENA_SHIFT 11 /* OPCLK_ENA */ +#define WM8400_OPCLK_ENA_WIDTH 1 /* OPCLK_ENA */ +#define WM8400_AINL_ENA 0x0200 /* AINL_ENA */ +#define WM8400_AINL_ENA_MASK 0x0200 /* AINL_ENA */ +#define WM8400_AINL_ENA_SHIFT 9 /* AINL_ENA */ +#define WM8400_AINL_ENA_WIDTH 1 /* AINL_ENA */ +#define WM8400_AINR_ENA 0x0100 /* AINR_ENA */ +#define WM8400_AINR_ENA_MASK 0x0100 /* AINR_ENA */ +#define WM8400_AINR_ENA_SHIFT 8 /* AINR_ENA */ +#define WM8400_AINR_ENA_WIDTH 1 /* AINR_ENA */ +#define WM8400_LIN34_ENA 0x0080 /* LIN34_ENA */ +#define WM8400_LIN34_ENA_MASK 0x0080 /* LIN34_ENA */ +#define WM8400_LIN34_ENA_SHIFT 7 /* LIN34_ENA */ +#define WM8400_LIN34_ENA_WIDTH 1 /* LIN34_ENA */ +#define WM8400_LIN12_ENA 0x0040 /* LIN12_ENA */ +#define WM8400_LIN12_ENA_MASK 0x0040 /* LIN12_ENA */ +#define WM8400_LIN12_ENA_SHIFT 6 /* LIN12_ENA */ +#define WM8400_LIN12_ENA_WIDTH 1 /* LIN12_ENA */ +#define WM8400_RIN34_ENA 0x0020 /* RIN34_ENA */ +#define WM8400_RIN34_ENA_MASK 0x0020 /* RIN34_ENA */ +#define WM8400_RIN34_ENA_SHIFT 5 /* RIN34_ENA */ +#define WM8400_RIN34_ENA_WIDTH 1 /* RIN34_ENA */ +#define WM8400_RIN12_ENA 0x0010 /* RIN12_ENA */ +#define WM8400_RIN12_ENA_MASK 0x0010 /* RIN12_ENA */ +#define WM8400_RIN12_ENA_SHIFT 4 /* RIN12_ENA */ +#define WM8400_RIN12_ENA_WIDTH 1 /* RIN12_ENA */ +#define WM8400_ADCL_ENA 0x0002 /* ADCL_ENA */ +#define WM8400_ADCL_ENA_MASK 0x0002 /* ADCL_ENA */ +#define WM8400_ADCL_ENA_SHIFT 1 /* ADCL_ENA */ +#define WM8400_ADCL_ENA_WIDTH 1 /* ADCL_ENA */ +#define WM8400_ADCR_ENA 0x0001 /* ADCR_ENA */ +#define WM8400_ADCR_ENA_MASK 0x0001 /* ADCR_ENA */ +#define WM8400_ADCR_ENA_SHIFT 0 /* ADCR_ENA */ +#define WM8400_ADCR_ENA_WIDTH 1 /* ADCR_ENA */ + +/* + * R4 (0x04) - Power Management (3) + */ +#define WM8400_LON_ENA 0x2000 /* LON_ENA */ +#define WM8400_LON_ENA_MASK 0x2000 /* LON_ENA */ +#define WM8400_LON_ENA_SHIFT 13 /* LON_ENA */ +#define WM8400_LON_ENA_WIDTH 1 /* LON_ENA */ +#define WM8400_LOP_ENA 0x1000 /* LOP_ENA */ +#define WM8400_LOP_ENA_MASK 0x1000 /* LOP_ENA */ +#define WM8400_LOP_ENA_SHIFT 12 /* LOP_ENA */ +#define WM8400_LOP_ENA_WIDTH 1 /* LOP_ENA */ +#define WM8400_RON_ENA 0x0800 /* RON_ENA */ +#define WM8400_RON_ENA_MASK 0x0800 /* RON_ENA */ +#define WM8400_RON_ENA_SHIFT 11 /* RON_ENA */ +#define WM8400_RON_ENA_WIDTH 1 /* RON_ENA */ +#define WM8400_ROP_ENA 0x0400 /* ROP_ENA */ +#define WM8400_ROP_ENA_MASK 0x0400 /* ROP_ENA */ +#define WM8400_ROP_ENA_SHIFT 10 /* ROP_ENA */ +#define WM8400_ROP_ENA_WIDTH 1 /* ROP_ENA */ +#define WM8400_LOPGA_ENA 0x0080 /* LOPGA_ENA */ +#define WM8400_LOPGA_ENA_MASK 0x0080 /* LOPGA_ENA */ +#define WM8400_LOPGA_ENA_SHIFT 7 /* LOPGA_ENA */ +#define WM8400_LOPGA_ENA_WIDTH 1 /* LOPGA_ENA */ +#define WM8400_ROPGA_ENA 0x0040 /* ROPGA_ENA */ +#define WM8400_ROPGA_ENA_MASK 0x0040 /* ROPGA_ENA */ +#define WM8400_ROPGA_ENA_SHIFT 6 /* ROPGA_ENA */ +#define WM8400_ROPGA_ENA_WIDTH 1 /* ROPGA_ENA */ +#define WM8400_LOMIX_ENA 0x0020 /* LOMIX_ENA */ +#define WM8400_LOMIX_ENA_MASK 0x0020 /* LOMIX_ENA */ +#define WM8400_LOMIX_ENA_SHIFT 5 /* LOMIX_ENA */ +#define WM8400_LOMIX_ENA_WIDTH 1 /* LOMIX_ENA */ +#define WM8400_ROMIX_ENA 0x0010 /* ROMIX_ENA */ +#define WM8400_ROMIX_ENA_MASK 0x0010 /* ROMIX_ENA */ +#define WM8400_ROMIX_ENA_SHIFT 4 /* ROMIX_ENA */ +#define WM8400_ROMIX_ENA_WIDTH 1 /* ROMIX_ENA */ +#define WM8400_DACL_ENA 0x0002 /* DACL_ENA */ +#define WM8400_DACL_ENA_MASK 0x0002 /* DACL_ENA */ +#define WM8400_DACL_ENA_SHIFT 1 /* DACL_ENA */ +#define WM8400_DACL_ENA_WIDTH 1 /* DACL_ENA */ +#define WM8400_DACR_ENA 0x0001 /* DACR_ENA */ +#define WM8400_DACR_ENA_MASK 0x0001 /* DACR_ENA */ +#define WM8400_DACR_ENA_SHIFT 0 /* DACR_ENA */ +#define WM8400_DACR_ENA_WIDTH 1 /* DACR_ENA */ + +/* + * R5 (0x05) - Audio Interface (1) + */ +#define WM8400_AIFADCL_SRC 0x8000 /* AIFADCL_SRC */ +#define WM8400_AIFADCL_SRC_MASK 0x8000 /* AIFADCL_SRC */ +#define WM8400_AIFADCL_SRC_SHIFT 15 /* AIFADCL_SRC */ +#define WM8400_AIFADCL_SRC_WIDTH 1 /* AIFADCL_SRC */ +#define WM8400_AIFADCR_SRC 0x4000 /* AIFADCR_SRC */ +#define WM8400_AIFADCR_SRC_MASK 0x4000 /* AIFADCR_SRC */ +#define WM8400_AIFADCR_SRC_SHIFT 14 /* AIFADCR_SRC */ +#define WM8400_AIFADCR_SRC_WIDTH 1 /* AIFADCR_SRC */ +#define WM8400_AIFADC_TDM 0x2000 /* AIFADC_TDM */ +#define WM8400_AIFADC_TDM_MASK 0x2000 /* AIFADC_TDM */ +#define WM8400_AIFADC_TDM_SHIFT 13 /* AIFADC_TDM */ +#define WM8400_AIFADC_TDM_WIDTH 1 /* AIFADC_TDM */ +#define WM8400_AIFADC_TDM_CHAN 0x1000 /* AIFADC_TDM_CHAN */ +#define WM8400_AIFADC_TDM_CHAN_MASK 0x1000 /* AIFADC_TDM_CHAN */ +#define WM8400_AIFADC_TDM_CHAN_SHIFT 12 /* AIFADC_TDM_CHAN */ +#define WM8400_AIFADC_TDM_CHAN_WIDTH 1 /* AIFADC_TDM_CHAN */ +#define WM8400_AIF_BCLK_INV 0x0100 /* AIF_BCLK_INV */ +#define WM8400_AIF_BCLK_INV_MASK 0x0100 /* AIF_BCLK_INV */ +#define WM8400_AIF_BCLK_INV_SHIFT 8 /* AIF_BCLK_INV */ +#define WM8400_AIF_BCLK_INV_WIDTH 1 /* AIF_BCLK_INV */ +#define WM8400_AIF_LRCLK_INV 0x0080 /* AIF_LRCLK_INV */ +#define WM8400_AIF_LRCLK_INV_MASK 0x0080 /* AIF_LRCLK_INV */ +#define WM8400_AIF_LRCLK_INV_SHIFT 7 /* AIF_LRCLK_INV */ +#define WM8400_AIF_LRCLK_INV_WIDTH 1 /* AIF_LRCLK_INV */ +#define WM8400_AIF_WL_MASK 0x0060 /* AIF_WL - [6:5] */ +#define WM8400_AIF_WL_SHIFT 5 /* AIF_WL - [6:5] */ +#define WM8400_AIF_WL_WIDTH 2 /* AIF_WL - [6:5] */ +#define WM8400_AIF_WL_16BITS (0 << 5) +#define WM8400_AIF_WL_20BITS (1 << 5) +#define WM8400_AIF_WL_24BITS (2 << 5) +#define WM8400_AIF_WL_32BITS (3 << 5) +#define WM8400_AIF_FMT_MASK 0x0018 /* AIF_FMT - [4:3] */ +#define WM8400_AIF_FMT_SHIFT 3 /* AIF_FMT - [4:3] */ +#define WM8400_AIF_FMT_WIDTH 2 /* AIF_FMT - [4:3] */ +#define WM8400_AIF_FMT_RIGHTJ (0 << 3) +#define WM8400_AIF_FMT_LEFTJ (1 << 3) +#define WM8400_AIF_FMT_I2S (2 << 3) +#define WM8400_AIF_FMT_DSP (3 << 3) + +/* + * R6 (0x06) - Audio Interface (2) + */ +#define WM8400_DACL_SRC 0x8000 /* DACL_SRC */ +#define WM8400_DACL_SRC_MASK 0x8000 /* DACL_SRC */ +#define WM8400_DACL_SRC_SHIFT 15 /* DACL_SRC */ +#define WM8400_DACL_SRC_WIDTH 1 /* DACL_SRC */ +#define WM8400_DACR_SRC 0x4000 /* DACR_SRC */ +#define WM8400_DACR_SRC_MASK 0x4000 /* DACR_SRC */ +#define WM8400_DACR_SRC_SHIFT 14 /* DACR_SRC */ +#define WM8400_DACR_SRC_WIDTH 1 /* DACR_SRC */ +#define WM8400_AIFDAC_TDM 0x2000 /* AIFDAC_TDM */ +#define WM8400_AIFDAC_TDM_MASK 0x2000 /* AIFDAC_TDM */ +#define WM8400_AIFDAC_TDM_SHIFT 13 /* AIFDAC_TDM */ +#define WM8400_AIFDAC_TDM_WIDTH 1 /* AIFDAC_TDM */ +#define WM8400_AIFDAC_TDM_CHAN 0x1000 /* AIFDAC_TDM_CHAN */ +#define WM8400_AIFDAC_TDM_CHAN_MASK 0x1000 /* AIFDAC_TDM_CHAN */ +#define WM8400_AIFDAC_TDM_CHAN_SHIFT 12 /* AIFDAC_TDM_CHAN */ +#define WM8400_AIFDAC_TDM_CHAN_WIDTH 1 /* AIFDAC_TDM_CHAN */ +#define WM8400_DAC_BOOST_MASK 0x0C00 /* DAC_BOOST - [11:10] */ +#define WM8400_DAC_BOOST_SHIFT 10 /* DAC_BOOST - [11:10] */ +#define WM8400_DAC_BOOST_WIDTH 2 /* DAC_BOOST - [11:10] */ +#define WM8400_DAC_COMP 0x0010 /* DAC_COMP */ +#define WM8400_DAC_COMP_MASK 0x0010 /* DAC_COMP */ +#define WM8400_DAC_COMP_SHIFT 4 /* DAC_COMP */ +#define WM8400_DAC_COMP_WIDTH 1 /* DAC_COMP */ +#define WM8400_DAC_COMPMODE 0x0008 /* DAC_COMPMODE */ +#define WM8400_DAC_COMPMODE_MASK 0x0008 /* DAC_COMPMODE */ +#define WM8400_DAC_COMPMODE_SHIFT 3 /* DAC_COMPMODE */ +#define WM8400_DAC_COMPMODE_WIDTH 1 /* DAC_COMPMODE */ +#define WM8400_ADC_COMP 0x0004 /* ADC_COMP */ +#define WM8400_ADC_COMP_MASK 0x0004 /* ADC_COMP */ +#define WM8400_ADC_COMP_SHIFT 2 /* ADC_COMP */ +#define WM8400_ADC_COMP_WIDTH 1 /* ADC_COMP */ +#define WM8400_ADC_COMPMODE 0x0002 /* ADC_COMPMODE */ +#define WM8400_ADC_COMPMODE_MASK 0x0002 /* ADC_COMPMODE */ +#define WM8400_ADC_COMPMODE_SHIFT 1 /* ADC_COMPMODE */ +#define WM8400_ADC_COMPMODE_WIDTH 1 /* ADC_COMPMODE */ +#define WM8400_LOOPBACK 0x0001 /* LOOPBACK */ +#define WM8400_LOOPBACK_MASK 0x0001 /* LOOPBACK */ +#define WM8400_LOOPBACK_SHIFT 0 /* LOOPBACK */ +#define WM8400_LOOPBACK_WIDTH 1 /* LOOPBACK */ + +/* + * R7 (0x07) - Clocking (1) + */ +#define WM8400_TOCLK_RATE 0x8000 /* TOCLK_RATE */ +#define WM8400_TOCLK_RATE_MASK 0x8000 /* TOCLK_RATE */ +#define WM8400_TOCLK_RATE_SHIFT 15 /* TOCLK_RATE */ +#define WM8400_TOCLK_RATE_WIDTH 1 /* TOCLK_RATE */ +#define WM8400_TOCLK_ENA 0x4000 /* TOCLK_ENA */ +#define WM8400_TOCLK_ENA_MASK 0x4000 /* TOCLK_ENA */ +#define WM8400_TOCLK_ENA_SHIFT 14 /* TOCLK_ENA */ +#define WM8400_TOCLK_ENA_WIDTH 1 /* TOCLK_ENA */ +#define WM8400_OPCLKDIV_MASK 0x1E00 /* OPCLKDIV - [12:9] */ +#define WM8400_OPCLKDIV_SHIFT 9 /* OPCLKDIV - [12:9] */ +#define WM8400_OPCLKDIV_WIDTH 4 /* OPCLKDIV - [12:9] */ +#define WM8400_DCLKDIV_MASK 0x01C0 /* DCLKDIV - [8:6] */ +#define WM8400_DCLKDIV_SHIFT 6 /* DCLKDIV - [8:6] */ +#define WM8400_DCLKDIV_WIDTH 3 /* DCLKDIV - [8:6] */ +#define WM8400_BCLK_DIV_MASK 0x001E /* BCLK_DIV - [4:1] */ +#define WM8400_BCLK_DIV_SHIFT 1 /* BCLK_DIV - [4:1] */ +#define WM8400_BCLK_DIV_WIDTH 4 /* BCLK_DIV - [4:1] */ + +/* + * R8 (0x08) - Clocking (2) + */ +#define WM8400_MCLK_SRC 0x8000 /* MCLK_SRC */ +#define WM8400_MCLK_SRC_MASK 0x8000 /* MCLK_SRC */ +#define WM8400_MCLK_SRC_SHIFT 15 /* MCLK_SRC */ +#define WM8400_MCLK_SRC_WIDTH 1 /* MCLK_SRC */ +#define WM8400_SYSCLK_SRC 0x4000 /* SYSCLK_SRC */ +#define WM8400_SYSCLK_SRC_MASK 0x4000 /* SYSCLK_SRC */ +#define WM8400_SYSCLK_SRC_SHIFT 14 /* SYSCLK_SRC */ +#define WM8400_SYSCLK_SRC_WIDTH 1 /* SYSCLK_SRC */ +#define WM8400_CLK_FORCE 0x2000 /* CLK_FORCE */ +#define WM8400_CLK_FORCE_MASK 0x2000 /* CLK_FORCE */ +#define WM8400_CLK_FORCE_SHIFT 13 /* CLK_FORCE */ +#define WM8400_CLK_FORCE_WIDTH 1 /* CLK_FORCE */ +#define WM8400_MCLK_DIV_MASK 0x1800 /* MCLK_DIV - [12:11] */ +#define WM8400_MCLK_DIV_SHIFT 11 /* MCLK_DIV - [12:11] */ +#define WM8400_MCLK_DIV_WIDTH 2 /* MCLK_DIV - [12:11] */ +#define WM8400_MCLK_INV 0x0400 /* MCLK_INV */ +#define WM8400_MCLK_INV_MASK 0x0400 /* MCLK_INV */ +#define WM8400_MCLK_INV_SHIFT 10 /* MCLK_INV */ +#define WM8400_MCLK_INV_WIDTH 1 /* MCLK_INV */ +#define WM8400_ADC_CLKDIV_MASK 0x00E0 /* ADC_CLKDIV - [7:5] */ +#define WM8400_ADC_CLKDIV_SHIFT 5 /* ADC_CLKDIV - [7:5] */ +#define WM8400_ADC_CLKDIV_WIDTH 3 /* ADC_CLKDIV - [7:5] */ +#define WM8400_DAC_CLKDIV_MASK 0x001C /* DAC_CLKDIV - [4:2] */ +#define WM8400_DAC_CLKDIV_SHIFT 2 /* DAC_CLKDIV - [4:2] */ +#define WM8400_DAC_CLKDIV_WIDTH 3 /* DAC_CLKDIV - [4:2] */ + +/* + * R9 (0x09) - Audio Interface (3) + */ +#define WM8400_AIF_MSTR1 0x8000 /* AIF_MSTR1 */ +#define WM8400_AIF_MSTR1_MASK 0x8000 /* AIF_MSTR1 */ +#define WM8400_AIF_MSTR1_SHIFT 15 /* AIF_MSTR1 */ +#define WM8400_AIF_MSTR1_WIDTH 1 /* AIF_MSTR1 */ +#define WM8400_AIF_MSTR2 0x4000 /* AIF_MSTR2 */ +#define WM8400_AIF_MSTR2_MASK 0x4000 /* AIF_MSTR2 */ +#define WM8400_AIF_MSTR2_SHIFT 14 /* AIF_MSTR2 */ +#define WM8400_AIF_MSTR2_WIDTH 1 /* AIF_MSTR2 */ +#define WM8400_AIF_SEL 0x2000 /* AIF_SEL */ +#define WM8400_AIF_SEL_MASK 0x2000 /* AIF_SEL */ +#define WM8400_AIF_SEL_SHIFT 13 /* AIF_SEL */ +#define WM8400_AIF_SEL_WIDTH 1 /* AIF_SEL */ +#define WM8400_ADCLRC_DIR 0x0800 /* ADCLRC_DIR */ +#define WM8400_ADCLRC_DIR_MASK 0x0800 /* ADCLRC_DIR */ +#define WM8400_ADCLRC_DIR_SHIFT 11 /* ADCLRC_DIR */ +#define WM8400_ADCLRC_DIR_WIDTH 1 /* ADCLRC_DIR */ +#define WM8400_ADCLRC_RATE_MASK 0x07FF /* ADCLRC_RATE - [10:0] */ +#define WM8400_ADCLRC_RATE_SHIFT 0 /* ADCLRC_RATE - [10:0] */ +#define WM8400_ADCLRC_RATE_WIDTH 11 /* ADCLRC_RATE - [10:0] */ + +/* + * R10 (0x0A) - Audio Interface (4) + */ +#define WM8400_ALRCGPIO1 0x8000 /* ALRCGPIO1 */ +#define WM8400_ALRCGPIO1_MASK 0x8000 /* ALRCGPIO1 */ +#define WM8400_ALRCGPIO1_SHIFT 15 /* ALRCGPIO1 */ +#define WM8400_ALRCGPIO1_WIDTH 1 /* ALRCGPIO1 */ +#define WM8400_ALRCBGPIO6 0x4000 /* ALRCBGPIO6 */ +#define WM8400_ALRCBGPIO6_MASK 0x4000 /* ALRCBGPIO6 */ +#define WM8400_ALRCBGPIO6_SHIFT 14 /* ALRCBGPIO6 */ +#define WM8400_ALRCBGPIO6_WIDTH 1 /* ALRCBGPIO6 */ +#define WM8400_AIF_TRIS 0x2000 /* AIF_TRIS */ +#define WM8400_AIF_TRIS_MASK 0x2000 /* AIF_TRIS */ +#define WM8400_AIF_TRIS_SHIFT 13 /* AIF_TRIS */ +#define WM8400_AIF_TRIS_WIDTH 1 /* AIF_TRIS */ +#define WM8400_DACLRC_DIR 0x0800 /* DACLRC_DIR */ +#define WM8400_DACLRC_DIR_MASK 0x0800 /* DACLRC_DIR */ +#define WM8400_DACLRC_DIR_SHIFT 11 /* DACLRC_DIR */ +#define WM8400_DACLRC_DIR_WIDTH 1 /* DACLRC_DIR */ +#define WM8400_DACLRC_RATE_MASK 0x07FF /* DACLRC_RATE - [10:0] */ +#define WM8400_DACLRC_RATE_SHIFT 0 /* DACLRC_RATE - [10:0] */ +#define WM8400_DACLRC_RATE_WIDTH 11 /* DACLRC_RATE - [10:0] */ + +/* + * R11 (0x0B) - DAC CTRL + */ +#define WM8400_DAC_SDMCLK_RATE 0x2000 /* DAC_SDMCLK_RATE */ +#define WM8400_DAC_SDMCLK_RATE_MASK 0x2000 /* DAC_SDMCLK_RATE */ +#define WM8400_DAC_SDMCLK_RATE_SHIFT 13 /* DAC_SDMCLK_RATE */ +#define WM8400_DAC_SDMCLK_RATE_WIDTH 1 /* DAC_SDMCLK_RATE */ +#define WM8400_AIF_LRCLKRATE 0x0400 /* AIF_LRCLKRATE */ +#define WM8400_AIF_LRCLKRATE_MASK 0x0400 /* AIF_LRCLKRATE */ +#define WM8400_AIF_LRCLKRATE_SHIFT 10 /* AIF_LRCLKRATE */ +#define WM8400_AIF_LRCLKRATE_WIDTH 1 /* AIF_LRCLKRATE */ +#define WM8400_DAC_MONO 0x0200 /* DAC_MONO */ +#define WM8400_DAC_MONO_MASK 0x0200 /* DAC_MONO */ +#define WM8400_DAC_MONO_SHIFT 9 /* DAC_MONO */ +#define WM8400_DAC_MONO_WIDTH 1 /* DAC_MONO */ +#define WM8400_DAC_SB_FILT 0x0100 /* DAC_SB_FILT */ +#define WM8400_DAC_SB_FILT_MASK 0x0100 /* DAC_SB_FILT */ +#define WM8400_DAC_SB_FILT_SHIFT 8 /* DAC_SB_FILT */ +#define WM8400_DAC_SB_FILT_WIDTH 1 /* DAC_SB_FILT */ +#define WM8400_DAC_MUTERATE 0x0080 /* DAC_MUTERATE */ +#define WM8400_DAC_MUTERATE_MASK 0x0080 /* DAC_MUTERATE */ +#define WM8400_DAC_MUTERATE_SHIFT 7 /* DAC_MUTERATE */ +#define WM8400_DAC_MUTERATE_WIDTH 1 /* DAC_MUTERATE */ +#define WM8400_DAC_MUTEMODE 0x0040 /* DAC_MUTEMODE */ +#define WM8400_DAC_MUTEMODE_MASK 0x0040 /* DAC_MUTEMODE */ +#define WM8400_DAC_MUTEMODE_SHIFT 6 /* DAC_MUTEMODE */ +#define WM8400_DAC_MUTEMODE_WIDTH 1 /* DAC_MUTEMODE */ +#define WM8400_DEEMP_MASK 0x0030 /* DEEMP - [5:4] */ +#define WM8400_DEEMP_SHIFT 4 /* DEEMP - [5:4] */ +#define WM8400_DEEMP_WIDTH 2 /* DEEMP - [5:4] */ +#define WM8400_DAC_MUTE 0x0004 /* DAC_MUTE */ +#define WM8400_DAC_MUTE_MASK 0x0004 /* DAC_MUTE */ +#define WM8400_DAC_MUTE_SHIFT 2 /* DAC_MUTE */ +#define WM8400_DAC_MUTE_WIDTH 1 /* DAC_MUTE */ +#define WM8400_DACL_DATINV 0x0002 /* DACL_DATINV */ +#define WM8400_DACL_DATINV_MASK 0x0002 /* DACL_DATINV */ +#define WM8400_DACL_DATINV_SHIFT 1 /* DACL_DATINV */ +#define WM8400_DACL_DATINV_WIDTH 1 /* DACL_DATINV */ +#define WM8400_DACR_DATINV 0x0001 /* DACR_DATINV */ +#define WM8400_DACR_DATINV_MASK 0x0001 /* DACR_DATINV */ +#define WM8400_DACR_DATINV_SHIFT 0 /* DACR_DATINV */ +#define WM8400_DACR_DATINV_WIDTH 1 /* DACR_DATINV */ + +/* + * R12 (0x0C) - Left DAC Digital Volume + */ +#define WM8400_DAC_VU 0x0100 /* DAC_VU */ +#define WM8400_DAC_VU_MASK 0x0100 /* DAC_VU */ +#define WM8400_DAC_VU_SHIFT 8 /* DAC_VU */ +#define WM8400_DAC_VU_WIDTH 1 /* DAC_VU */ +#define WM8400_DACL_VOL_MASK 0x00FF /* DACL_VOL - [7:0] */ +#define WM8400_DACL_VOL_SHIFT 0 /* DACL_VOL - [7:0] */ +#define WM8400_DACL_VOL_WIDTH 8 /* DACL_VOL - [7:0] */ + +/* + * R13 (0x0D) - Right DAC Digital Volume + */ +#define WM8400_DAC_VU 0x0100 /* DAC_VU */ +#define WM8400_DAC_VU_MASK 0x0100 /* DAC_VU */ +#define WM8400_DAC_VU_SHIFT 8 /* DAC_VU */ +#define WM8400_DAC_VU_WIDTH 1 /* DAC_VU */ +#define WM8400_DACR_VOL_MASK 0x00FF /* DACR_VOL - [7:0] */ +#define WM8400_DACR_VOL_SHIFT 0 /* DACR_VOL - [7:0] */ +#define WM8400_DACR_VOL_WIDTH 8 /* DACR_VOL - [7:0] */ + +/* + * R14 (0x0E) - Digital Side Tone + */ +#define WM8400_ADCL_DAC_SVOL_MASK 0x1E00 /* ADCL_DAC_SVOL - [12:9] */ +#define WM8400_ADCL_DAC_SVOL_SHIFT 9 /* ADCL_DAC_SVOL - [12:9] */ +#define WM8400_ADCL_DAC_SVOL_WIDTH 4 /* ADCL_DAC_SVOL - [12:9] */ +#define WM8400_ADCR_DAC_SVOL_MASK 0x01E0 /* ADCR_DAC_SVOL - [8:5] */ +#define WM8400_ADCR_DAC_SVOL_SHIFT 5 /* ADCR_DAC_SVOL - [8:5] */ +#define WM8400_ADCR_DAC_SVOL_WIDTH 4 /* ADCR_DAC_SVOL - [8:5] */ +#define WM8400_ADC_TO_DACL_MASK 0x000C /* ADC_TO_DACL - [3:2] */ +#define WM8400_ADC_TO_DACL_SHIFT 2 /* ADC_TO_DACL - [3:2] */ +#define WM8400_ADC_TO_DACL_WIDTH 2 /* ADC_TO_DACL - [3:2] */ +#define WM8400_ADC_TO_DACR_MASK 0x0003 /* ADC_TO_DACR - [1:0] */ +#define WM8400_ADC_TO_DACR_SHIFT 0 /* ADC_TO_DACR - [1:0] */ +#define WM8400_ADC_TO_DACR_WIDTH 2 /* ADC_TO_DACR - [1:0] */ + +/* + * R15 (0x0F) - ADC CTRL + */ +#define WM8400_ADC_HPF_ENA 0x0100 /* ADC_HPF_ENA */ +#define WM8400_ADC_HPF_ENA_MASK 0x0100 /* ADC_HPF_ENA */ +#define WM8400_ADC_HPF_ENA_SHIFT 8 /* ADC_HPF_ENA */ +#define WM8400_ADC_HPF_ENA_WIDTH 1 /* ADC_HPF_ENA */ +#define WM8400_ADC_HPF_CUT_MASK 0x0060 /* ADC_HPF_CUT - [6:5] */ +#define WM8400_ADC_HPF_CUT_SHIFT 5 /* ADC_HPF_CUT - [6:5] */ +#define WM8400_ADC_HPF_CUT_WIDTH 2 /* ADC_HPF_CUT - [6:5] */ +#define WM8400_ADCL_DATINV 0x0002 /* ADCL_DATINV */ +#define WM8400_ADCL_DATINV_MASK 0x0002 /* ADCL_DATINV */ +#define WM8400_ADCL_DATINV_SHIFT 1 /* ADCL_DATINV */ +#define WM8400_ADCL_DATINV_WIDTH 1 /* ADCL_DATINV */ +#define WM8400_ADCR_DATINV 0x0001 /* ADCR_DATINV */ +#define WM8400_ADCR_DATINV_MASK 0x0001 /* ADCR_DATINV */ +#define WM8400_ADCR_DATINV_SHIFT 0 /* ADCR_DATINV */ +#define WM8400_ADCR_DATINV_WIDTH 1 /* ADCR_DATINV */ + +/* + * R16 (0x10) - Left ADC Digital Volume + */ +#define WM8400_ADC_VU 0x0100 /* ADC_VU */ +#define WM8400_ADC_VU_MASK 0x0100 /* ADC_VU */ +#define WM8400_ADC_VU_SHIFT 8 /* ADC_VU */ +#define WM8400_ADC_VU_WIDTH 1 /* ADC_VU */ +#define WM8400_ADCL_VOL_MASK 0x00FF /* ADCL_VOL - [7:0] */ +#define WM8400_ADCL_VOL_SHIFT 0 /* ADCL_VOL - [7:0] */ +#define WM8400_ADCL_VOL_WIDTH 8 /* ADCL_VOL - [7:0] */ + +/* + * R17 (0x11) - Right ADC Digital Volume + */ +#define WM8400_ADC_VU 0x0100 /* ADC_VU */ +#define WM8400_ADC_VU_MASK 0x0100 /* ADC_VU */ +#define WM8400_ADC_VU_SHIFT 8 /* ADC_VU */ +#define WM8400_ADC_VU_WIDTH 1 /* ADC_VU */ +#define WM8400_ADCR_VOL_MASK 0x00FF /* ADCR_VOL - [7:0] */ +#define WM8400_ADCR_VOL_SHIFT 0 /* ADCR_VOL - [7:0] */ +#define WM8400_ADCR_VOL_WIDTH 8 /* ADCR_VOL - [7:0] */ + +/* + * R24 (0x18) - Left Line Input 1&2 Volume + */ +#define WM8400_IPVU 0x0100 /* IPVU */ +#define WM8400_IPVU_MASK 0x0100 /* IPVU */ +#define WM8400_IPVU_SHIFT 8 /* IPVU */ +#define WM8400_IPVU_WIDTH 1 /* IPVU */ +#define WM8400_LI12MUTE 0x0080 /* LI12MUTE */ +#define WM8400_LI12MUTE_MASK 0x0080 /* LI12MUTE */ +#define WM8400_LI12MUTE_SHIFT 7 /* LI12MUTE */ +#define WM8400_LI12MUTE_WIDTH 1 /* LI12MUTE */ +#define WM8400_LI12ZC 0x0040 /* LI12ZC */ +#define WM8400_LI12ZC_MASK 0x0040 /* LI12ZC */ +#define WM8400_LI12ZC_SHIFT 6 /* LI12ZC */ +#define WM8400_LI12ZC_WIDTH 1 /* LI12ZC */ +#define WM8400_LIN12VOL_MASK 0x001F /* LIN12VOL - [4:0] */ +#define WM8400_LIN12VOL_SHIFT 0 /* LIN12VOL - [4:0] */ +#define WM8400_LIN12VOL_WIDTH 5 /* LIN12VOL - [4:0] */ + +/* + * R25 (0x19) - Left Line Input 3&4 Volume + */ +#define WM8400_IPVU 0x0100 /* IPVU */ +#define WM8400_IPVU_MASK 0x0100 /* IPVU */ +#define WM8400_IPVU_SHIFT 8 /* IPVU */ +#define WM8400_IPVU_WIDTH 1 /* IPVU */ +#define WM8400_LI34MUTE 0x0080 /* LI34MUTE */ +#define WM8400_LI34MUTE_MASK 0x0080 /* LI34MUTE */ +#define WM8400_LI34MUTE_SHIFT 7 /* LI34MUTE */ +#define WM8400_LI34MUTE_WIDTH 1 /* LI34MUTE */ +#define WM8400_LI34ZC 0x0040 /* LI34ZC */ +#define WM8400_LI34ZC_MASK 0x0040 /* LI34ZC */ +#define WM8400_LI34ZC_SHIFT 6 /* LI34ZC */ +#define WM8400_LI34ZC_WIDTH 1 /* LI34ZC */ +#define WM8400_LIN34VOL_MASK 0x001F /* LIN34VOL - [4:0] */ +#define WM8400_LIN34VOL_SHIFT 0 /* LIN34VOL - [4:0] */ +#define WM8400_LIN34VOL_WIDTH 5 /* LIN34VOL - [4:0] */ + +/* + * R26 (0x1A) - Right Line Input 1&2 Volume + */ +#define WM8400_IPVU 0x0100 /* IPVU */ +#define WM8400_IPVU_MASK 0x0100 /* IPVU */ +#define WM8400_IPVU_SHIFT 8 /* IPVU */ +#define WM8400_IPVU_WIDTH 1 /* IPVU */ +#define WM8400_RI12MUTE 0x0080 /* RI12MUTE */ +#define WM8400_RI12MUTE_MASK 0x0080 /* RI12MUTE */ +#define WM8400_RI12MUTE_SHIFT 7 /* RI12MUTE */ +#define WM8400_RI12MUTE_WIDTH 1 /* RI12MUTE */ +#define WM8400_RI12ZC 0x0040 /* RI12ZC */ +#define WM8400_RI12ZC_MASK 0x0040 /* RI12ZC */ +#define WM8400_RI12ZC_SHIFT 6 /* RI12ZC */ +#define WM8400_RI12ZC_WIDTH 1 /* RI12ZC */ +#define WM8400_RIN12VOL_MASK 0x001F /* RIN12VOL - [4:0] */ +#define WM8400_RIN12VOL_SHIFT 0 /* RIN12VOL - [4:0] */ +#define WM8400_RIN12VOL_WIDTH 5 /* RIN12VOL - [4:0] */ + +/* + * R27 (0x1B) - Right Line Input 3&4 Volume + */ +#define WM8400_IPVU 0x0100 /* IPVU */ +#define WM8400_IPVU_MASK 0x0100 /* IPVU */ +#define WM8400_IPVU_SHIFT 8 /* IPVU */ +#define WM8400_IPVU_WIDTH 1 /* IPVU */ +#define WM8400_RI34MUTE 0x0080 /* RI34MUTE */ +#define WM8400_RI34MUTE_MASK 0x0080 /* RI34MUTE */ +#define WM8400_RI34MUTE_SHIFT 7 /* RI34MUTE */ +#define WM8400_RI34MUTE_WIDTH 1 /* RI34MUTE */ +#define WM8400_RI34ZC 0x0040 /* RI34ZC */ +#define WM8400_RI34ZC_MASK 0x0040 /* RI34ZC */ +#define WM8400_RI34ZC_SHIFT 6 /* RI34ZC */ +#define WM8400_RI34ZC_WIDTH 1 /* RI34ZC */ +#define WM8400_RIN34VOL_MASK 0x001F /* RIN34VOL - [4:0] */ +#define WM8400_RIN34VOL_SHIFT 0 /* RIN34VOL - [4:0] */ +#define WM8400_RIN34VOL_WIDTH 5 /* RIN34VOL - [4:0] */ + +/* + * R28 (0x1C) - Left Output Volume + */ +#define WM8400_OPVU 0x0100 /* OPVU */ +#define WM8400_OPVU_MASK 0x0100 /* OPVU */ +#define WM8400_OPVU_SHIFT 8 /* OPVU */ +#define WM8400_OPVU_WIDTH 1 /* OPVU */ +#define WM8400_LOZC 0x0080 /* LOZC */ +#define WM8400_LOZC_MASK 0x0080 /* LOZC */ +#define WM8400_LOZC_SHIFT 7 /* LOZC */ +#define WM8400_LOZC_WIDTH 1 /* LOZC */ +#define WM8400_LOUTVOL_MASK 0x007F /* LOUTVOL - [6:0] */ +#define WM8400_LOUTVOL_SHIFT 0 /* LOUTVOL - [6:0] */ +#define WM8400_LOUTVOL_WIDTH 7 /* LOUTVOL - [6:0] */ + +/* + * R29 (0x1D) - Right Output Volume + */ +#define WM8400_OPVU 0x0100 /* OPVU */ +#define WM8400_OPVU_MASK 0x0100 /* OPVU */ +#define WM8400_OPVU_SHIFT 8 /* OPVU */ +#define WM8400_OPVU_WIDTH 1 /* OPVU */ +#define WM8400_ROZC 0x0080 /* ROZC */ +#define WM8400_ROZC_MASK 0x0080 /* ROZC */ +#define WM8400_ROZC_SHIFT 7 /* ROZC */ +#define WM8400_ROZC_WIDTH 1 /* ROZC */ +#define WM8400_ROUTVOL_MASK 0x007F /* ROUTVOL - [6:0] */ +#define WM8400_ROUTVOL_SHIFT 0 /* ROUTVOL - [6:0] */ +#define WM8400_ROUTVOL_WIDTH 7 /* ROUTVOL - [6:0] */ + +/* + * R30 (0x1E) - Line Outputs Volume + */ +#define WM8400_LONMUTE 0x0040 /* LONMUTE */ +#define WM8400_LONMUTE_MASK 0x0040 /* LONMUTE */ +#define WM8400_LONMUTE_SHIFT 6 /* LONMUTE */ +#define WM8400_LONMUTE_WIDTH 1 /* LONMUTE */ +#define WM8400_LOPMUTE 0x0020 /* LOPMUTE */ +#define WM8400_LOPMUTE_MASK 0x0020 /* LOPMUTE */ +#define WM8400_LOPMUTE_SHIFT 5 /* LOPMUTE */ +#define WM8400_LOPMUTE_WIDTH 1 /* LOPMUTE */ +#define WM8400_LOATTN 0x0010 /* LOATTN */ +#define WM8400_LOATTN_MASK 0x0010 /* LOATTN */ +#define WM8400_LOATTN_SHIFT 4 /* LOATTN */ +#define WM8400_LOATTN_WIDTH 1 /* LOATTN */ +#define WM8400_RONMUTE 0x0004 /* RONMUTE */ +#define WM8400_RONMUTE_MASK 0x0004 /* RONMUTE */ +#define WM8400_RONMUTE_SHIFT 2 /* RONMUTE */ +#define WM8400_RONMUTE_WIDTH 1 /* RONMUTE */ +#define WM8400_ROPMUTE 0x0002 /* ROPMUTE */ +#define WM8400_ROPMUTE_MASK 0x0002 /* ROPMUTE */ +#define WM8400_ROPMUTE_SHIFT 1 /* ROPMUTE */ +#define WM8400_ROPMUTE_WIDTH 1 /* ROPMUTE */ +#define WM8400_ROATTN 0x0001 /* ROATTN */ +#define WM8400_ROATTN_MASK 0x0001 /* ROATTN */ +#define WM8400_ROATTN_SHIFT 0 /* ROATTN */ +#define WM8400_ROATTN_WIDTH 1 /* ROATTN */ + +/* + * R31 (0x1F) - Out3/4 Volume + */ +#define WM8400_OUT3MUTE 0x0020 /* OUT3MUTE */ +#define WM8400_OUT3MUTE_MASK 0x0020 /* OUT3MUTE */ +#define WM8400_OUT3MUTE_SHIFT 5 /* OUT3MUTE */ +#define WM8400_OUT3MUTE_WIDTH 1 /* OUT3MUTE */ +#define WM8400_OUT3ATTN 0x0010 /* OUT3ATTN */ +#define WM8400_OUT3ATTN_MASK 0x0010 /* OUT3ATTN */ +#define WM8400_OUT3ATTN_SHIFT 4 /* OUT3ATTN */ +#define WM8400_OUT3ATTN_WIDTH 1 /* OUT3ATTN */ +#define WM8400_OUT4MUTE 0x0002 /* OUT4MUTE */ +#define WM8400_OUT4MUTE_MASK 0x0002 /* OUT4MUTE */ +#define WM8400_OUT4MUTE_SHIFT 1 /* OUT4MUTE */ +#define WM8400_OUT4MUTE_WIDTH 1 /* OUT4MUTE */ +#define WM8400_OUT4ATTN 0x0001 /* OUT4ATTN */ +#define WM8400_OUT4ATTN_MASK 0x0001 /* OUT4ATTN */ +#define WM8400_OUT4ATTN_SHIFT 0 /* OUT4ATTN */ +#define WM8400_OUT4ATTN_WIDTH 1 /* OUT4ATTN */ + +/* + * R32 (0x20) - Left OPGA Volume + */ +#define WM8400_OPVU 0x0100 /* OPVU */ +#define WM8400_OPVU_MASK 0x0100 /* OPVU */ +#define WM8400_OPVU_SHIFT 8 /* OPVU */ +#define WM8400_OPVU_WIDTH 1 /* OPVU */ +#define WM8400_LOPGAZC 0x0080 /* LOPGAZC */ +#define WM8400_LOPGAZC_MASK 0x0080 /* LOPGAZC */ +#define WM8400_LOPGAZC_SHIFT 7 /* LOPGAZC */ +#define WM8400_LOPGAZC_WIDTH 1 /* LOPGAZC */ +#define WM8400_LOPGAVOL_MASK 0x007F /* LOPGAVOL - [6:0] */ +#define WM8400_LOPGAVOL_SHIFT 0 /* LOPGAVOL - [6:0] */ +#define WM8400_LOPGAVOL_WIDTH 7 /* LOPGAVOL - [6:0] */ + +/* + * R33 (0x21) - Right OPGA Volume + */ +#define WM8400_OPVU 0x0100 /* OPVU */ +#define WM8400_OPVU_MASK 0x0100 /* OPVU */ +#define WM8400_OPVU_SHIFT 8 /* OPVU */ +#define WM8400_OPVU_WIDTH 1 /* OPVU */ +#define WM8400_ROPGAZC 0x0080 /* ROPGAZC */ +#define WM8400_ROPGAZC_MASK 0x0080 /* ROPGAZC */ +#define WM8400_ROPGAZC_SHIFT 7 /* ROPGAZC */ +#define WM8400_ROPGAZC_WIDTH 1 /* ROPGAZC */ +#define WM8400_ROPGAVOL_MASK 0x007F /* ROPGAVOL - [6:0] */ +#define WM8400_ROPGAVOL_SHIFT 0 /* ROPGAVOL - [6:0] */ +#define WM8400_ROPGAVOL_WIDTH 7 /* ROPGAVOL - [6:0] */ + +/* + * R34 (0x22) - Speaker Volume + */ +#define WM8400_SPKATTN_MASK 0x0003 /* SPKATTN - [1:0] */ +#define WM8400_SPKATTN_SHIFT 0 /* SPKATTN - [1:0] */ +#define WM8400_SPKATTN_WIDTH 2 /* SPKATTN - [1:0] */ + +/* + * R35 (0x23) - ClassD1 + */ +#define WM8400_CDMODE 0x0100 /* CDMODE */ +#define WM8400_CDMODE_MASK 0x0100 /* CDMODE */ +#define WM8400_CDMODE_SHIFT 8 /* CDMODE */ +#define WM8400_CDMODE_WIDTH 1 /* CDMODE */ +#define WM8400_CLASSD_CLK_SEL 0x0080 /* CLASSD_CLK_SEL */ +#define WM8400_CLASSD_CLK_SEL_MASK 0x0080 /* CLASSD_CLK_SEL */ +#define WM8400_CLASSD_CLK_SEL_SHIFT 7 /* CLASSD_CLK_SEL */ +#define WM8400_CLASSD_CLK_SEL_WIDTH 1 /* CLASSD_CLK_SEL */ +#define WM8400_CD_SRCTRL 0x0040 /* CD_SRCTRL */ +#define WM8400_CD_SRCTRL_MASK 0x0040 /* CD_SRCTRL */ +#define WM8400_CD_SRCTRL_SHIFT 6 /* CD_SRCTRL */ +#define WM8400_CD_SRCTRL_WIDTH 1 /* CD_SRCTRL */ +#define WM8400_SPKNOPOP 0x0020 /* SPKNOPOP */ +#define WM8400_SPKNOPOP_MASK 0x0020 /* SPKNOPOP */ +#define WM8400_SPKNOPOP_SHIFT 5 /* SPKNOPOP */ +#define WM8400_SPKNOPOP_WIDTH 1 /* SPKNOPOP */ +#define WM8400_DBLERATE 0x0010 /* DBLERATE */ +#define WM8400_DBLERATE_MASK 0x0010 /* DBLERATE */ +#define WM8400_DBLERATE_SHIFT 4 /* DBLERATE */ +#define WM8400_DBLERATE_WIDTH 1 /* DBLERATE */ +#define WM8400_LOOPTEST 0x0008 /* LOOPTEST */ +#define WM8400_LOOPTEST_MASK 0x0008 /* LOOPTEST */ +#define WM8400_LOOPTEST_SHIFT 3 /* LOOPTEST */ +#define WM8400_LOOPTEST_WIDTH 1 /* LOOPTEST */ +#define WM8400_HALFABBIAS 0x0004 /* HALFABBIAS */ +#define WM8400_HALFABBIAS_MASK 0x0004 /* HALFABBIAS */ +#define WM8400_HALFABBIAS_SHIFT 2 /* HALFABBIAS */ +#define WM8400_HALFABBIAS_WIDTH 1 /* HALFABBIAS */ +#define WM8400_TRIDEL_MASK 0x0003 /* TRIDEL - [1:0] */ +#define WM8400_TRIDEL_SHIFT 0 /* TRIDEL - [1:0] */ +#define WM8400_TRIDEL_WIDTH 2 /* TRIDEL - [1:0] */ + +/* + * R37 (0x25) - ClassD3 + */ +#define WM8400_DCGAIN_MASK 0x0038 /* DCGAIN - [5:3] */ +#define WM8400_DCGAIN_SHIFT 3 /* DCGAIN - [5:3] */ +#define WM8400_DCGAIN_WIDTH 3 /* DCGAIN - [5:3] */ +#define WM8400_ACGAIN_MASK 0x0007 /* ACGAIN - [2:0] */ +#define WM8400_ACGAIN_SHIFT 0 /* ACGAIN - [2:0] */ +#define WM8400_ACGAIN_WIDTH 3 /* ACGAIN - [2:0] */ + +/* + * R39 (0x27) - Input Mixer1 + */ +#define WM8400_AINLMODE_MASK 0x000C /* AINLMODE - [3:2] */ +#define WM8400_AINLMODE_SHIFT 2 /* AINLMODE - [3:2] */ +#define WM8400_AINLMODE_WIDTH 2 /* AINLMODE - [3:2] */ +#define WM8400_AINRMODE_MASK 0x0003 /* AINRMODE - [1:0] */ +#define WM8400_AINRMODE_SHIFT 0 /* AINRMODE - [1:0] */ +#define WM8400_AINRMODE_WIDTH 2 /* AINRMODE - [1:0] */ + +/* + * R40 (0x28) - Input Mixer2 + */ +#define WM8400_LMP4 0x0080 /* LMP4 */ +#define WM8400_LMP4_MASK 0x0080 /* LMP4 */ +#define WM8400_LMP4_SHIFT 7 /* LMP4 */ +#define WM8400_LMP4_WIDTH 1 /* LMP4 */ +#define WM8400_LMN3 0x0040 /* LMN3 */ +#define WM8400_LMN3_MASK 0x0040 /* LMN3 */ +#define WM8400_LMN3_SHIFT 6 /* LMN3 */ +#define WM8400_LMN3_WIDTH 1 /* LMN3 */ +#define WM8400_LMP2 0x0020 /* LMP2 */ +#define WM8400_LMP2_MASK 0x0020 /* LMP2 */ +#define WM8400_LMP2_SHIFT 5 /* LMP2 */ +#define WM8400_LMP2_WIDTH 1 /* LMP2 */ +#define WM8400_LMN1 0x0010 /* LMN1 */ +#define WM8400_LMN1_MASK 0x0010 /* LMN1 */ +#define WM8400_LMN1_SHIFT 4 /* LMN1 */ +#define WM8400_LMN1_WIDTH 1 /* LMN1 */ +#define WM8400_RMP4 0x0008 /* RMP4 */ +#define WM8400_RMP4_MASK 0x0008 /* RMP4 */ +#define WM8400_RMP4_SHIFT 3 /* RMP4 */ +#define WM8400_RMP4_WIDTH 1 /* RMP4 */ +#define WM8400_RMN3 0x0004 /* RMN3 */ +#define WM8400_RMN3_MASK 0x0004 /* RMN3 */ +#define WM8400_RMN3_SHIFT 2 /* RMN3 */ +#define WM8400_RMN3_WIDTH 1 /* RMN3 */ +#define WM8400_RMP2 0x0002 /* RMP2 */ +#define WM8400_RMP2_MASK 0x0002 /* RMP2 */ +#define WM8400_RMP2_SHIFT 1 /* RMP2 */ +#define WM8400_RMP2_WIDTH 1 /* RMP2 */ +#define WM8400_RMN1 0x0001 /* RMN1 */ +#define WM8400_RMN1_MASK 0x0001 /* RMN1 */ +#define WM8400_RMN1_SHIFT 0 /* RMN1 */ +#define WM8400_RMN1_WIDTH 1 /* RMN1 */ + +/* + * R41 (0x29) - Input Mixer3 + */ +#define WM8400_L34MNB 0x0100 /* L34MNB */ +#define WM8400_L34MNB_MASK 0x0100 /* L34MNB */ +#define WM8400_L34MNB_SHIFT 8 /* L34MNB */ +#define WM8400_L34MNB_WIDTH 1 /* L34MNB */ +#define WM8400_L34MNBST 0x0080 /* L34MNBST */ +#define WM8400_L34MNBST_MASK 0x0080 /* L34MNBST */ +#define WM8400_L34MNBST_SHIFT 7 /* L34MNBST */ +#define WM8400_L34MNBST_WIDTH 1 /* L34MNBST */ +#define WM8400_L12MNB 0x0020 /* L12MNB */ +#define WM8400_L12MNB_MASK 0x0020 /* L12MNB */ +#define WM8400_L12MNB_SHIFT 5 /* L12MNB */ +#define WM8400_L12MNB_WIDTH 1 /* L12MNB */ +#define WM8400_L12MNBST 0x0010 /* L12MNBST */ +#define WM8400_L12MNBST_MASK 0x0010 /* L12MNBST */ +#define WM8400_L12MNBST_SHIFT 4 /* L12MNBST */ +#define WM8400_L12MNBST_WIDTH 1 /* L12MNBST */ +#define WM8400_LDBVOL_MASK 0x0007 /* LDBVOL - [2:0] */ +#define WM8400_LDBVOL_SHIFT 0 /* LDBVOL - [2:0] */ +#define WM8400_LDBVOL_WIDTH 3 /* LDBVOL - [2:0] */ + +/* + * R42 (0x2A) - Input Mixer4 + */ +#define WM8400_R34MNB 0x0100 /* R34MNB */ +#define WM8400_R34MNB_MASK 0x0100 /* R34MNB */ +#define WM8400_R34MNB_SHIFT 8 /* R34MNB */ +#define WM8400_R34MNB_WIDTH 1 /* R34MNB */ +#define WM8400_R34MNBST 0x0080 /* R34MNBST */ +#define WM8400_R34MNBST_MASK 0x0080 /* R34MNBST */ +#define WM8400_R34MNBST_SHIFT 7 /* R34MNBST */ +#define WM8400_R34MNBST_WIDTH 1 /* R34MNBST */ +#define WM8400_R12MNB 0x0020 /* R12MNB */ +#define WM8400_R12MNB_MASK 0x0020 /* R12MNB */ +#define WM8400_R12MNB_SHIFT 5 /* R12MNB */ +#define WM8400_R12MNB_WIDTH 1 /* R12MNB */ +#define WM8400_R12MNBST 0x0010 /* R12MNBST */ +#define WM8400_R12MNBST_MASK 0x0010 /* R12MNBST */ +#define WM8400_R12MNBST_SHIFT 4 /* R12MNBST */ +#define WM8400_R12MNBST_WIDTH 1 /* R12MNBST */ +#define WM8400_RDBVOL_MASK 0x0007 /* RDBVOL - [2:0] */ +#define WM8400_RDBVOL_SHIFT 0 /* RDBVOL - [2:0] */ +#define WM8400_RDBVOL_WIDTH 3 /* RDBVOL - [2:0] */ + +/* + * R43 (0x2B) - Input Mixer5 + */ +#define WM8400_LI2BVOL_MASK 0x01C0 /* LI2BVOL - [8:6] */ +#define WM8400_LI2BVOL_SHIFT 6 /* LI2BVOL - [8:6] */ +#define WM8400_LI2BVOL_WIDTH 3 /* LI2BVOL - [8:6] */ +#define WM8400_LR4BVOL_MASK 0x0038 /* LR4BVOL - [5:3] */ +#define WM8400_LR4BVOL_SHIFT 3 /* LR4BVOL - [5:3] */ +#define WM8400_LR4BVOL_WIDTH 3 /* LR4BVOL - [5:3] */ +#define WM8400_LL4BVOL_MASK 0x0007 /* LL4BVOL - [2:0] */ +#define WM8400_LL4BVOL_SHIFT 0 /* LL4BVOL - [2:0] */ +#define WM8400_LL4BVOL_WIDTH 3 /* LL4BVOL - [2:0] */ + +/* + * R44 (0x2C) - Input Mixer6 + */ +#define WM8400_RI2BVOL_MASK 0x01C0 /* RI2BVOL - [8:6] */ +#define WM8400_RI2BVOL_SHIFT 6 /* RI2BVOL - [8:6] */ +#define WM8400_RI2BVOL_WIDTH 3 /* RI2BVOL - [8:6] */ +#define WM8400_RL4BVOL_MASK 0x0038 /* RL4BVOL - [5:3] */ +#define WM8400_RL4BVOL_SHIFT 3 /* RL4BVOL - [5:3] */ +#define WM8400_RL4BVOL_WIDTH 3 /* RL4BVOL - [5:3] */ +#define WM8400_RR4BVOL_MASK 0x0007 /* RR4BVOL - [2:0] */ +#define WM8400_RR4BVOL_SHIFT 0 /* RR4BVOL - [2:0] */ +#define WM8400_RR4BVOL_WIDTH 3 /* RR4BVOL - [2:0] */ + +/* + * R45 (0x2D) - Output Mixer1 + */ +#define WM8400_LRBLO 0x0080 /* LRBLO */ +#define WM8400_LRBLO_MASK 0x0080 /* LRBLO */ +#define WM8400_LRBLO_SHIFT 7 /* LRBLO */ +#define WM8400_LRBLO_WIDTH 1 /* LRBLO */ +#define WM8400_LLBLO 0x0040 /* LLBLO */ +#define WM8400_LLBLO_MASK 0x0040 /* LLBLO */ +#define WM8400_LLBLO_SHIFT 6 /* LLBLO */ +#define WM8400_LLBLO_WIDTH 1 /* LLBLO */ +#define WM8400_LRI3LO 0x0020 /* LRI3LO */ +#define WM8400_LRI3LO_MASK 0x0020 /* LRI3LO */ +#define WM8400_LRI3LO_SHIFT 5 /* LRI3LO */ +#define WM8400_LRI3LO_WIDTH 1 /* LRI3LO */ +#define WM8400_LLI3LO 0x0010 /* LLI3LO */ +#define WM8400_LLI3LO_MASK 0x0010 /* LLI3LO */ +#define WM8400_LLI3LO_SHIFT 4 /* LLI3LO */ +#define WM8400_LLI3LO_WIDTH 1 /* LLI3LO */ +#define WM8400_LR12LO 0x0008 /* LR12LO */ +#define WM8400_LR12LO_MASK 0x0008 /* LR12LO */ +#define WM8400_LR12LO_SHIFT 3 /* LR12LO */ +#define WM8400_LR12LO_WIDTH 1 /* LR12LO */ +#define WM8400_LL12LO 0x0004 /* LL12LO */ +#define WM8400_LL12LO_MASK 0x0004 /* LL12LO */ +#define WM8400_LL12LO_SHIFT 2 /* LL12LO */ +#define WM8400_LL12LO_WIDTH 1 /* LL12LO */ +#define WM8400_LDLO 0x0001 /* LDLO */ +#define WM8400_LDLO_MASK 0x0001 /* LDLO */ +#define WM8400_LDLO_SHIFT 0 /* LDLO */ +#define WM8400_LDLO_WIDTH 1 /* LDLO */ + +/* + * R46 (0x2E) - Output Mixer2 + */ +#define WM8400_RLBRO 0x0080 /* RLBRO */ +#define WM8400_RLBRO_MASK 0x0080 /* RLBRO */ +#define WM8400_RLBRO_SHIFT 7 /* RLBRO */ +#define WM8400_RLBRO_WIDTH 1 /* RLBRO */ +#define WM8400_RRBRO 0x0040 /* RRBRO */ +#define WM8400_RRBRO_MASK 0x0040 /* RRBRO */ +#define WM8400_RRBRO_SHIFT 6 /* RRBRO */ +#define WM8400_RRBRO_WIDTH 1 /* RRBRO */ +#define WM8400_RLI3RO 0x0020 /* RLI3RO */ +#define WM8400_RLI3RO_MASK 0x0020 /* RLI3RO */ +#define WM8400_RLI3RO_SHIFT 5 /* RLI3RO */ +#define WM8400_RLI3RO_WIDTH 1 /* RLI3RO */ +#define WM8400_RRI3RO 0x0010 /* RRI3RO */ +#define WM8400_RRI3RO_MASK 0x0010 /* RRI3RO */ +#define WM8400_RRI3RO_SHIFT 4 /* RRI3RO */ +#define WM8400_RRI3RO_WIDTH 1 /* RRI3RO */ +#define WM8400_RL12RO 0x0008 /* RL12RO */ +#define WM8400_RL12RO_MASK 0x0008 /* RL12RO */ +#define WM8400_RL12RO_SHIFT 3 /* RL12RO */ +#define WM8400_RL12RO_WIDTH 1 /* RL12RO */ +#define WM8400_RR12RO 0x0004 /* RR12RO */ +#define WM8400_RR12RO_MASK 0x0004 /* RR12RO */ +#define WM8400_RR12RO_SHIFT 2 /* RR12RO */ +#define WM8400_RR12RO_WIDTH 1 /* RR12RO */ +#define WM8400_RDRO 0x0001 /* RDRO */ +#define WM8400_RDRO_MASK 0x0001 /* RDRO */ +#define WM8400_RDRO_SHIFT 0 /* RDRO */ +#define WM8400_RDRO_WIDTH 1 /* RDRO */ + +/* + * R47 (0x2F) - Output Mixer3 + */ +#define WM8400_LLI3LOVOL_MASK 0x01C0 /* LLI3LOVOL - [8:6] */ +#define WM8400_LLI3LOVOL_SHIFT 6 /* LLI3LOVOL - [8:6] */ +#define WM8400_LLI3LOVOL_WIDTH 3 /* LLI3LOVOL - [8:6] */ +#define WM8400_LR12LOVOL_MASK 0x0038 /* LR12LOVOL - [5:3] */ +#define WM8400_LR12LOVOL_SHIFT 3 /* LR12LOVOL - [5:3] */ +#define WM8400_LR12LOVOL_WIDTH 3 /* LR12LOVOL - [5:3] */ +#define WM8400_LL12LOVOL_MASK 0x0007 /* LL12LOVOL - [2:0] */ +#define WM8400_LL12LOVOL_SHIFT 0 /* LL12LOVOL - [2:0] */ +#define WM8400_LL12LOVOL_WIDTH 3 /* LL12LOVOL - [2:0] */ + +/* + * R48 (0x30) - Output Mixer4 + */ +#define WM8400_RRI3ROVOL_MASK 0x01C0 /* RRI3ROVOL - [8:6] */ +#define WM8400_RRI3ROVOL_SHIFT 6 /* RRI3ROVOL - [8:6] */ +#define WM8400_RRI3ROVOL_WIDTH 3 /* RRI3ROVOL - [8:6] */ +#define WM8400_RL12ROVOL_MASK 0x0038 /* RL12ROVOL - [5:3] */ +#define WM8400_RL12ROVOL_SHIFT 3 /* RL12ROVOL - [5:3] */ +#define WM8400_RL12ROVOL_WIDTH 3 /* RL12ROVOL - [5:3] */ +#define WM8400_RR12ROVOL_MASK 0x0007 /* RR12ROVOL - [2:0] */ +#define WM8400_RR12ROVOL_SHIFT 0 /* RR12ROVOL - [2:0] */ +#define WM8400_RR12ROVOL_WIDTH 3 /* RR12ROVOL - [2:0] */ + +/* + * R49 (0x31) - Output Mixer5 + */ +#define WM8400_LRI3LOVOL_MASK 0x01C0 /* LRI3LOVOL - [8:6] */ +#define WM8400_LRI3LOVOL_SHIFT 6 /* LRI3LOVOL - [8:6] */ +#define WM8400_LRI3LOVOL_WIDTH 3 /* LRI3LOVOL - [8:6] */ +#define WM8400_LRBLOVOL_MASK 0x0038 /* LRBLOVOL - [5:3] */ +#define WM8400_LRBLOVOL_SHIFT 3 /* LRBLOVOL - [5:3] */ +#define WM8400_LRBLOVOL_WIDTH 3 /* LRBLOVOL - [5:3] */ +#define WM8400_LLBLOVOL_MASK 0x0007 /* LLBLOVOL - [2:0] */ +#define WM8400_LLBLOVOL_SHIFT 0 /* LLBLOVOL - [2:0] */ +#define WM8400_LLBLOVOL_WIDTH 3 /* LLBLOVOL - [2:0] */ + +/* + * R50 (0x32) - Output Mixer6 + */ +#define WM8400_RLI3ROVOL_MASK 0x01C0 /* RLI3ROVOL - [8:6] */ +#define WM8400_RLI3ROVOL_SHIFT 6 /* RLI3ROVOL - [8:6] */ +#define WM8400_RLI3ROVOL_WIDTH 3 /* RLI3ROVOL - [8:6] */ +#define WM8400_RLBROVOL_MASK 0x0038 /* RLBROVOL - [5:3] */ +#define WM8400_RLBROVOL_SHIFT 3 /* RLBROVOL - [5:3] */ +#define WM8400_RLBROVOL_WIDTH 3 /* RLBROVOL - [5:3] */ +#define WM8400_RRBROVOL_MASK 0x0007 /* RRBROVOL - [2:0] */ +#define WM8400_RRBROVOL_SHIFT 0 /* RRBROVOL - [2:0] */ +#define WM8400_RRBROVOL_WIDTH 3 /* RRBROVOL - [2:0] */ + +/* + * R51 (0x33) - Out3/4 Mixer + */ +#define WM8400_VSEL_MASK 0x0180 /* VSEL - [8:7] */ +#define WM8400_VSEL_SHIFT 7 /* VSEL - [8:7] */ +#define WM8400_VSEL_WIDTH 2 /* VSEL - [8:7] */ +#define WM8400_LI4O3 0x0020 /* LI4O3 */ +#define WM8400_LI4O3_MASK 0x0020 /* LI4O3 */ +#define WM8400_LI4O3_SHIFT 5 /* LI4O3 */ +#define WM8400_LI4O3_WIDTH 1 /* LI4O3 */ +#define WM8400_LPGAO3 0x0010 /* LPGAO3 */ +#define WM8400_LPGAO3_MASK 0x0010 /* LPGAO3 */ +#define WM8400_LPGAO3_SHIFT 4 /* LPGAO3 */ +#define WM8400_LPGAO3_WIDTH 1 /* LPGAO3 */ +#define WM8400_RI4O4 0x0002 /* RI4O4 */ +#define WM8400_RI4O4_MASK 0x0002 /* RI4O4 */ +#define WM8400_RI4O4_SHIFT 1 /* RI4O4 */ +#define WM8400_RI4O4_WIDTH 1 /* RI4O4 */ +#define WM8400_RPGAO4 0x0001 /* RPGAO4 */ +#define WM8400_RPGAO4_MASK 0x0001 /* RPGAO4 */ +#define WM8400_RPGAO4_SHIFT 0 /* RPGAO4 */ +#define WM8400_RPGAO4_WIDTH 1 /* RPGAO4 */ + +/* + * R52 (0x34) - Line Mixer1 + */ +#define WM8400_LLOPGALON 0x0040 /* LLOPGALON */ +#define WM8400_LLOPGALON_MASK 0x0040 /* LLOPGALON */ +#define WM8400_LLOPGALON_SHIFT 6 /* LLOPGALON */ +#define WM8400_LLOPGALON_WIDTH 1 /* LLOPGALON */ +#define WM8400_LROPGALON 0x0020 /* LROPGALON */ +#define WM8400_LROPGALON_MASK 0x0020 /* LROPGALON */ +#define WM8400_LROPGALON_SHIFT 5 /* LROPGALON */ +#define WM8400_LROPGALON_WIDTH 1 /* LROPGALON */ +#define WM8400_LOPLON 0x0010 /* LOPLON */ +#define WM8400_LOPLON_MASK 0x0010 /* LOPLON */ +#define WM8400_LOPLON_SHIFT 4 /* LOPLON */ +#define WM8400_LOPLON_WIDTH 1 /* LOPLON */ +#define WM8400_LR12LOP 0x0004 /* LR12LOP */ +#define WM8400_LR12LOP_MASK 0x0004 /* LR12LOP */ +#define WM8400_LR12LOP_SHIFT 2 /* LR12LOP */ +#define WM8400_LR12LOP_WIDTH 1 /* LR12LOP */ +#define WM8400_LL12LOP 0x0002 /* LL12LOP */ +#define WM8400_LL12LOP_MASK 0x0002 /* LL12LOP */ +#define WM8400_LL12LOP_SHIFT 1 /* LL12LOP */ +#define WM8400_LL12LOP_WIDTH 1 /* LL12LOP */ +#define WM8400_LLOPGALOP 0x0001 /* LLOPGALOP */ +#define WM8400_LLOPGALOP_MASK 0x0001 /* LLOPGALOP */ +#define WM8400_LLOPGALOP_SHIFT 0 /* LLOPGALOP */ +#define WM8400_LLOPGALOP_WIDTH 1 /* LLOPGALOP */ + +/* + * R53 (0x35) - Line Mixer2 + */ +#define WM8400_RROPGARON 0x0040 /* RROPGARON */ +#define WM8400_RROPGARON_MASK 0x0040 /* RROPGARON */ +#define WM8400_RROPGARON_SHIFT 6 /* RROPGARON */ +#define WM8400_RROPGARON_WIDTH 1 /* RROPGARON */ +#define WM8400_RLOPGARON 0x0020 /* RLOPGARON */ +#define WM8400_RLOPGARON_MASK 0x0020 /* RLOPGARON */ +#define WM8400_RLOPGARON_SHIFT 5 /* RLOPGARON */ +#define WM8400_RLOPGARON_WIDTH 1 /* RLOPGARON */ +#define WM8400_ROPRON 0x0010 /* ROPRON */ +#define WM8400_ROPRON_MASK 0x0010 /* ROPRON */ +#define WM8400_ROPRON_SHIFT 4 /* ROPRON */ +#define WM8400_ROPRON_WIDTH 1 /* ROPRON */ +#define WM8400_RL12ROP 0x0004 /* RL12ROP */ +#define WM8400_RL12ROP_MASK 0x0004 /* RL12ROP */ +#define WM8400_RL12ROP_SHIFT 2 /* RL12ROP */ +#define WM8400_RL12ROP_WIDTH 1 /* RL12ROP */ +#define WM8400_RR12ROP 0x0002 /* RR12ROP */ +#define WM8400_RR12ROP_MASK 0x0002 /* RR12ROP */ +#define WM8400_RR12ROP_SHIFT 1 /* RR12ROP */ +#define WM8400_RR12ROP_WIDTH 1 /* RR12ROP */ +#define WM8400_RROPGAROP 0x0001 /* RROPGAROP */ +#define WM8400_RROPGAROP_MASK 0x0001 /* RROPGAROP */ +#define WM8400_RROPGAROP_SHIFT 0 /* RROPGAROP */ +#define WM8400_RROPGAROP_WIDTH 1 /* RROPGAROP */ + +/* + * R54 (0x36) - Speaker Mixer + */ +#define WM8400_LB2SPK 0x0080 /* LB2SPK */ +#define WM8400_LB2SPK_MASK 0x0080 /* LB2SPK */ +#define WM8400_LB2SPK_SHIFT 7 /* LB2SPK */ +#define WM8400_LB2SPK_WIDTH 1 /* LB2SPK */ +#define WM8400_RB2SPK 0x0040 /* RB2SPK */ +#define WM8400_RB2SPK_MASK 0x0040 /* RB2SPK */ +#define WM8400_RB2SPK_SHIFT 6 /* RB2SPK */ +#define WM8400_RB2SPK_WIDTH 1 /* RB2SPK */ +#define WM8400_LI2SPK 0x0020 /* LI2SPK */ +#define WM8400_LI2SPK_MASK 0x0020 /* LI2SPK */ +#define WM8400_LI2SPK_SHIFT 5 /* LI2SPK */ +#define WM8400_LI2SPK_WIDTH 1 /* LI2SPK */ +#define WM8400_RI2SPK 0x0010 /* RI2SPK */ +#define WM8400_RI2SPK_MASK 0x0010 /* RI2SPK */ +#define WM8400_RI2SPK_SHIFT 4 /* RI2SPK */ +#define WM8400_RI2SPK_WIDTH 1 /* RI2SPK */ +#define WM8400_LOPGASPK 0x0008 /* LOPGASPK */ +#define WM8400_LOPGASPK_MASK 0x0008 /* LOPGASPK */ +#define WM8400_LOPGASPK_SHIFT 3 /* LOPGASPK */ +#define WM8400_LOPGASPK_WIDTH 1 /* LOPGASPK */ +#define WM8400_ROPGASPK 0x0004 /* ROPGASPK */ +#define WM8400_ROPGASPK_MASK 0x0004 /* ROPGASPK */ +#define WM8400_ROPGASPK_SHIFT 2 /* ROPGASPK */ +#define WM8400_ROPGASPK_WIDTH 1 /* ROPGASPK */ +#define WM8400_LDSPK 0x0002 /* LDSPK */ +#define WM8400_LDSPK_MASK 0x0002 /* LDSPK */ +#define WM8400_LDSPK_SHIFT 1 /* LDSPK */ +#define WM8400_LDSPK_WIDTH 1 /* LDSPK */ +#define WM8400_RDSPK 0x0001 /* RDSPK */ +#define WM8400_RDSPK_MASK 0x0001 /* RDSPK */ +#define WM8400_RDSPK_SHIFT 0 /* RDSPK */ +#define WM8400_RDSPK_WIDTH 1 /* RDSPK */ + +/* + * R55 (0x37) - Additional Control + */ +#define WM8400_VROI 0x0001 /* VROI */ +#define WM8400_VROI_MASK 0x0001 /* VROI */ +#define WM8400_VROI_SHIFT 0 /* VROI */ +#define WM8400_VROI_WIDTH 1 /* VROI */ + +/* + * R56 (0x38) - AntiPOP1 + */ +#define WM8400_DIS_LLINE 0x0020 /* DIS_LLINE */ +#define WM8400_DIS_LLINE_MASK 0x0020 /* DIS_LLINE */ +#define WM8400_DIS_LLINE_SHIFT 5 /* DIS_LLINE */ +#define WM8400_DIS_LLINE_WIDTH 1 /* DIS_LLINE */ +#define WM8400_DIS_RLINE 0x0010 /* DIS_RLINE */ +#define WM8400_DIS_RLINE_MASK 0x0010 /* DIS_RLINE */ +#define WM8400_DIS_RLINE_SHIFT 4 /* DIS_RLINE */ +#define WM8400_DIS_RLINE_WIDTH 1 /* DIS_RLINE */ +#define WM8400_DIS_OUT3 0x0008 /* DIS_OUT3 */ +#define WM8400_DIS_OUT3_MASK 0x0008 /* DIS_OUT3 */ +#define WM8400_DIS_OUT3_SHIFT 3 /* DIS_OUT3 */ +#define WM8400_DIS_OUT3_WIDTH 1 /* DIS_OUT3 */ +#define WM8400_DIS_OUT4 0x0004 /* DIS_OUT4 */ +#define WM8400_DIS_OUT4_MASK 0x0004 /* DIS_OUT4 */ +#define WM8400_DIS_OUT4_SHIFT 2 /* DIS_OUT4 */ +#define WM8400_DIS_OUT4_WIDTH 1 /* DIS_OUT4 */ +#define WM8400_DIS_LOUT 0x0002 /* DIS_LOUT */ +#define WM8400_DIS_LOUT_MASK 0x0002 /* DIS_LOUT */ +#define WM8400_DIS_LOUT_SHIFT 1 /* DIS_LOUT */ +#define WM8400_DIS_LOUT_WIDTH 1 /* DIS_LOUT */ +#define WM8400_DIS_ROUT 0x0001 /* DIS_ROUT */ +#define WM8400_DIS_ROUT_MASK 0x0001 /* DIS_ROUT */ +#define WM8400_DIS_ROUT_SHIFT 0 /* DIS_ROUT */ +#define WM8400_DIS_ROUT_WIDTH 1 /* DIS_ROUT */ + +/* + * R57 (0x39) - AntiPOP2 + */ +#define WM8400_SOFTST 0x0040 /* SOFTST */ +#define WM8400_SOFTST_MASK 0x0040 /* SOFTST */ +#define WM8400_SOFTST_SHIFT 6 /* SOFTST */ +#define WM8400_SOFTST_WIDTH 1 /* SOFTST */ +#define WM8400_BUFIOEN 0x0008 /* BUFIOEN */ +#define WM8400_BUFIOEN_MASK 0x0008 /* BUFIOEN */ +#define WM8400_BUFIOEN_SHIFT 3 /* BUFIOEN */ +#define WM8400_BUFIOEN_WIDTH 1 /* BUFIOEN */ +#define WM8400_BUFDCOPEN 0x0004 /* BUFDCOPEN */ +#define WM8400_BUFDCOPEN_MASK 0x0004 /* BUFDCOPEN */ +#define WM8400_BUFDCOPEN_SHIFT 2 /* BUFDCOPEN */ +#define WM8400_BUFDCOPEN_WIDTH 1 /* BUFDCOPEN */ +#define WM8400_POBCTRL 0x0002 /* POBCTRL */ +#define WM8400_POBCTRL_MASK 0x0002 /* POBCTRL */ +#define WM8400_POBCTRL_SHIFT 1 /* POBCTRL */ +#define WM8400_POBCTRL_WIDTH 1 /* POBCTRL */ +#define WM8400_VMIDTOG 0x0001 /* VMIDTOG */ +#define WM8400_VMIDTOG_MASK 0x0001 /* VMIDTOG */ +#define WM8400_VMIDTOG_SHIFT 0 /* VMIDTOG */ +#define WM8400_VMIDTOG_WIDTH 1 /* VMIDTOG */ + +/* + * R58 (0x3A) - MICBIAS + */ +#define WM8400_MCDSCTH_MASK 0x00C0 /* MCDSCTH - [7:6] */ +#define WM8400_MCDSCTH_SHIFT 6 /* MCDSCTH - [7:6] */ +#define WM8400_MCDSCTH_WIDTH 2 /* MCDSCTH - [7:6] */ +#define WM8400_MCDTHR_MASK 0x0038 /* MCDTHR - [5:3] */ +#define WM8400_MCDTHR_SHIFT 3 /* MCDTHR - [5:3] */ +#define WM8400_MCDTHR_WIDTH 3 /* MCDTHR - [5:3] */ +#define WM8400_MCD 0x0004 /* MCD */ +#define WM8400_MCD_MASK 0x0004 /* MCD */ +#define WM8400_MCD_SHIFT 2 /* MCD */ +#define WM8400_MCD_WIDTH 1 /* MCD */ +#define WM8400_MBSEL 0x0001 /* MBSEL */ +#define WM8400_MBSEL_MASK 0x0001 /* MBSEL */ +#define WM8400_MBSEL_SHIFT 0 /* MBSEL */ +#define WM8400_MBSEL_WIDTH 1 /* MBSEL */ + +/* + * R60 (0x3C) - FLL Control 1 + */ +#define WM8400_FLL_REF_FREQ 0x1000 /* FLL_REF_FREQ */ +#define WM8400_FLL_REF_FREQ_MASK 0x1000 /* FLL_REF_FREQ */ +#define WM8400_FLL_REF_FREQ_SHIFT 12 /* FLL_REF_FREQ */ +#define WM8400_FLL_REF_FREQ_WIDTH 1 /* FLL_REF_FREQ */ +#define WM8400_FLL_CLK_SRC_MASK 0x0C00 /* FLL_CLK_SRC - [11:10] */ +#define WM8400_FLL_CLK_SRC_SHIFT 10 /* FLL_CLK_SRC - [11:10] */ +#define WM8400_FLL_CLK_SRC_WIDTH 2 /* FLL_CLK_SRC - [11:10] */ +#define WM8400_FLL_FRAC 0x0200 /* FLL_FRAC */ +#define WM8400_FLL_FRAC_MASK 0x0200 /* FLL_FRAC */ +#define WM8400_FLL_FRAC_SHIFT 9 /* FLL_FRAC */ +#define WM8400_FLL_FRAC_WIDTH 1 /* FLL_FRAC */ +#define WM8400_FLL_OSC_ENA 0x0100 /* FLL_OSC_ENA */ +#define WM8400_FLL_OSC_ENA_MASK 0x0100 /* FLL_OSC_ENA */ +#define WM8400_FLL_OSC_ENA_SHIFT 8 /* FLL_OSC_ENA */ +#define WM8400_FLL_OSC_ENA_WIDTH 1 /* FLL_OSC_ENA */ +#define WM8400_FLL_CTRL_RATE_MASK 0x00E0 /* FLL_CTRL_RATE - [7:5] */ +#define WM8400_FLL_CTRL_RATE_SHIFT 5 /* FLL_CTRL_RATE - [7:5] */ +#define WM8400_FLL_CTRL_RATE_WIDTH 3 /* FLL_CTRL_RATE - [7:5] */ +#define WM8400_FLL_FRATIO_MASK 0x001F /* FLL_FRATIO - [4:0] */ +#define WM8400_FLL_FRATIO_SHIFT 0 /* FLL_FRATIO - [4:0] */ +#define WM8400_FLL_FRATIO_WIDTH 5 /* FLL_FRATIO - [4:0] */ + +/* + * R61 (0x3D) - FLL Control 2 + */ +#define WM8400_FLL_K_MASK 0xFFFF /* FLL_K - [15:0] */ +#define WM8400_FLL_K_SHIFT 0 /* FLL_K - [15:0] */ +#define WM8400_FLL_K_WIDTH 16 /* FLL_K - [15:0] */ + +/* + * R62 (0x3E) - FLL Control 3 + */ +#define WM8400_FLL_N_MASK 0x03FF /* FLL_N - [9:0] */ +#define WM8400_FLL_N_SHIFT 0 /* FLL_N - [9:0] */ +#define WM8400_FLL_N_WIDTH 10 /* FLL_N - [9:0] */ + +/* + * R63 (0x3F) - FLL Control 4 + */ +#define WM8400_FLL_TRK_GAIN_MASK 0x0078 /* FLL_TRK_GAIN - [6:3] */ +#define WM8400_FLL_TRK_GAIN_SHIFT 3 /* FLL_TRK_GAIN - [6:3] */ +#define WM8400_FLL_TRK_GAIN_WIDTH 4 /* FLL_TRK_GAIN - [6:3] */ +#define WM8400_FLL_OUTDIV_MASK 0x0007 /* FLL_OUTDIV - [2:0] */ +#define WM8400_FLL_OUTDIV_SHIFT 0 /* FLL_OUTDIV - [2:0] */ +#define WM8400_FLL_OUTDIV_WIDTH 3 /* FLL_OUTDIV - [2:0] */ + +void wm8400_reset_codec_reg_cache(struct wm8400 *wm8400); + +#endif diff --git a/include/linux/mfd/wm8400-private.h b/include/linux/mfd/wm8400-private.h new file mode 100644 index 000000000000..2aab4e93a5c9 --- /dev/null +++ b/include/linux/mfd/wm8400-private.h @@ -0,0 +1,936 @@ +/* + * wm8400 private definitions. + * + * Copyright 2008 Wolfson Microelectronics plc + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#ifndef __LINUX_MFD_WM8400_PRIV_H +#define __LINUX_MFD_WM8400_PRIV_H + +#include <linux/mfd/wm8400.h> +#include <linux/mutex.h> +#include <linux/platform_device.h> + +#define WM8400_REGISTER_COUNT 0x55 + +struct wm8400 { + struct device *dev; + + int (*read_dev)(void *data, char reg, int count, u16 *dst); + int (*write_dev)(void *data, char reg, int count, const u16 *src); + + struct mutex io_lock; + void *io_data; + + u16 reg_cache[WM8400_REGISTER_COUNT]; + + struct platform_device regulators[6]; +}; + +/* + * Register values. + */ +#define WM8400_RESET_ID 0x00 +#define WM8400_ID 0x01 +#define WM8400_POWER_MANAGEMENT_1 0x02 +#define WM8400_POWER_MANAGEMENT_2 0x03 +#define WM8400_POWER_MANAGEMENT_3 0x04 +#define WM8400_AUDIO_INTERFACE_1 0x05 +#define WM8400_AUDIO_INTERFACE_2 0x06 +#define WM8400_CLOCKING_1 0x07 +#define WM8400_CLOCKING_2 0x08 +#define WM8400_AUDIO_INTERFACE_3 0x09 +#define WM8400_AUDIO_INTERFACE_4 0x0A +#define WM8400_DAC_CTRL 0x0B +#define WM8400_LEFT_DAC_DIGITAL_VOLUME 0x0C +#define WM8400_RIGHT_DAC_DIGITAL_VOLUME 0x0D +#define WM8400_DIGITAL_SIDE_TONE 0x0E +#define WM8400_ADC_CTRL 0x0F +#define WM8400_LEFT_ADC_DIGITAL_VOLUME 0x10 +#define WM8400_RIGHT_ADC_DIGITAL_VOLUME 0x11 +#define WM8400_GPIO_CTRL_1 0x12 +#define WM8400_GPIO1_GPIO2 0x13 +#define WM8400_GPIO3_GPIO4 0x14 +#define WM8400_GPIO5_GPIO6 0x15 +#define WM8400_GPIOCTRL_2 0x16 +#define WM8400_GPIO_POL 0x17 +#define WM8400_LEFT_LINE_INPUT_1_2_VOLUME 0x18 +#define WM8400_LEFT_LINE_INPUT_3_4_VOLUME 0x19 +#define WM8400_RIGHT_LINE_INPUT_1_2_VOLUME 0x1A +#define WM8400_RIGHT_LINE_INPUT_3_4_VOLUME 0x1B +#define WM8400_LEFT_OUTPUT_VOLUME 0x1C +#define WM8400_RIGHT_OUTPUT_VOLUME 0x1D +#define WM8400_LINE_OUTPUTS_VOLUME 0x1E +#define WM8400_OUT3_4_VOLUME 0x1F +#define WM8400_LEFT_OPGA_VOLUME 0x20 +#define WM8400_RIGHT_OPGA_VOLUME 0x21 +#define WM8400_SPEAKER_VOLUME 0x22 +#define WM8400_CLASSD1 0x23 +#define WM8400_CLASSD3 0x25 +#define WM8400_INPUT_MIXER1 0x27 +#define WM8400_INPUT_MIXER2 0x28 +#define WM8400_INPUT_MIXER3 0x29 +#define WM8400_INPUT_MIXER4 0x2A +#define WM8400_INPUT_MIXER5 0x2B +#define WM8400_INPUT_MIXER6 0x2C +#define WM8400_OUTPUT_MIXER1 0x2D +#define WM8400_OUTPUT_MIXER2 0x2E +#define WM8400_OUTPUT_MIXER3 0x2F +#define WM8400_OUTPUT_MIXER4 0x30 +#define WM8400_OUTPUT_MIXER5 0x31 +#define WM8400_OUTPUT_MIXER6 0x32 +#define WM8400_OUT3_4_MIXER 0x33 +#define WM8400_LINE_MIXER1 0x34 +#define WM8400_LINE_MIXER2 0x35 +#define WM8400_SPEAKER_MIXER 0x36 +#define WM8400_ADDITIONAL_CONTROL 0x37 +#define WM8400_ANTIPOP1 0x38 +#define WM8400_ANTIPOP2 0x39 +#define WM8400_MICBIAS 0x3A +#define WM8400_FLL_CONTROL_1 0x3C +#define WM8400_FLL_CONTROL_2 0x3D +#define WM8400_FLL_CONTROL_3 0x3E +#define WM8400_FLL_CONTROL_4 0x3F +#define WM8400_LDO1_CONTROL 0x41 +#define WM8400_LDO2_CONTROL 0x42 +#define WM8400_LDO3_CONTROL 0x43 +#define WM8400_LDO4_CONTROL 0x44 +#define WM8400_DCDC1_CONTROL_1 0x46 +#define WM8400_DCDC1_CONTROL_2 0x47 +#define WM8400_DCDC2_CONTROL_1 0x48 +#define WM8400_DCDC2_CONTROL_2 0x49 +#define WM8400_INTERFACE 0x4B +#define WM8400_PM_GENERAL 0x4C +#define WM8400_PM_SHUTDOWN_CONTROL 0x4E +#define WM8400_INTERRUPT_STATUS_1 0x4F +#define WM8400_INTERRUPT_STATUS_1_MASK 0x50 +#define WM8400_INTERRUPT_LEVELS 0x51 +#define WM8400_SHUTDOWN_REASON 0x52 +#define WM8400_LINE_CIRCUITS 0x54 + +/* + * Field Definitions. + */ + +/* + * R0 (0x00) - Reset/ID + */ +#define WM8400_SW_RESET_CHIP_ID_MASK 0xFFFF /* SW_RESET/CHIP_ID - [15:0] */ +#define WM8400_SW_RESET_CHIP_ID_SHIFT 0 /* SW_RESET/CHIP_ID - [15:0] */ +#define WM8400_SW_RESET_CHIP_ID_WIDTH 16 /* SW_RESET/CHIP_ID - [15:0] */ + +/* + * R1 (0x01) - ID + */ +#define WM8400_CHIP_REV_MASK 0x7000 /* CHIP_REV - [14:12] */ +#define WM8400_CHIP_REV_SHIFT 12 /* CHIP_REV - [14:12] */ +#define WM8400_CHIP_REV_WIDTH 3 /* CHIP_REV - [14:12] */ + +/* + * R18 (0x12) - GPIO CTRL 1 + */ +#define WM8400_IRQ 0x1000 /* IRQ */ +#define WM8400_IRQ_MASK 0x1000 /* IRQ */ +#define WM8400_IRQ_SHIFT 12 /* IRQ */ +#define WM8400_IRQ_WIDTH 1 /* IRQ */ +#define WM8400_TEMPOK 0x0800 /* TEMPOK */ +#define WM8400_TEMPOK_MASK 0x0800 /* TEMPOK */ +#define WM8400_TEMPOK_SHIFT 11 /* TEMPOK */ +#define WM8400_TEMPOK_WIDTH 1 /* TEMPOK */ +#define WM8400_MIC1SHRT 0x0400 /* MIC1SHRT */ +#define WM8400_MIC1SHRT_MASK 0x0400 /* MIC1SHRT */ +#define WM8400_MIC1SHRT_SHIFT 10 /* MIC1SHRT */ +#define WM8400_MIC1SHRT_WIDTH 1 /* MIC1SHRT */ +#define WM8400_MIC1DET 0x0200 /* MIC1DET */ +#define WM8400_MIC1DET_MASK 0x0200 /* MIC1DET */ +#define WM8400_MIC1DET_SHIFT 9 /* MIC1DET */ +#define WM8400_MIC1DET_WIDTH 1 /* MIC1DET */ +#define WM8400_FLL_LCK 0x0100 /* FLL_LCK */ +#define WM8400_FLL_LCK_MASK 0x0100 /* FLL_LCK */ +#define WM8400_FLL_LCK_SHIFT 8 /* FLL_LCK */ +#define WM8400_FLL_LCK_WIDTH 1 /* FLL_LCK */ +#define WM8400_GPIO_STATUS_MASK 0x00FF /* GPIO_STATUS - [7:0] */ +#define WM8400_GPIO_STATUS_SHIFT 0 /* GPIO_STATUS - [7:0] */ +#define WM8400_GPIO_STATUS_WIDTH 8 /* GPIO_STATUS - [7:0] */ + +/* + * R19 (0x13) - GPIO1 & GPIO2 + */ +#define WM8400_GPIO2_DEB_ENA 0x8000 /* GPIO2_DEB_ENA */ +#define WM8400_GPIO2_DEB_ENA_MASK 0x8000 /* GPIO2_DEB_ENA */ +#define WM8400_GPIO2_DEB_ENA_SHIFT 15 /* GPIO2_DEB_ENA */ +#define WM8400_GPIO2_DEB_ENA_WIDTH 1 /* GPIO2_DEB_ENA */ +#define WM8400_GPIO2_IRQ_ENA 0x4000 /* GPIO2_IRQ_ENA */ +#define WM8400_GPIO2_IRQ_ENA_MASK 0x4000 /* GPIO2_IRQ_ENA */ +#define WM8400_GPIO2_IRQ_ENA_SHIFT 14 /* GPIO2_IRQ_ENA */ +#define WM8400_GPIO2_IRQ_ENA_WIDTH 1 /* GPIO2_IRQ_ENA */ +#define WM8400_GPIO2_PU 0x2000 /* GPIO2_PU */ +#define WM8400_GPIO2_PU_MASK 0x2000 /* GPIO2_PU */ +#define WM8400_GPIO2_PU_SHIFT 13 /* GPIO2_PU */ +#define WM8400_GPIO2_PU_WIDTH 1 /* GPIO2_PU */ +#define WM8400_GPIO2_PD 0x1000 /* GPIO2_PD */ +#define WM8400_GPIO2_PD_MASK 0x1000 /* GPIO2_PD */ +#define WM8400_GPIO2_PD_SHIFT 12 /* GPIO2_PD */ +#define WM8400_GPIO2_PD_WIDTH 1 /* GPIO2_PD */ +#define WM8400_GPIO2_SEL_MASK 0x0F00 /* GPIO2_SEL - [11:8] */ +#define WM8400_GPIO2_SEL_SHIFT 8 /* GPIO2_SEL - [11:8] */ +#define WM8400_GPIO2_SEL_WIDTH 4 /* GPIO2_SEL - [11:8] */ +#define WM8400_GPIO1_DEB_ENA 0x0080 /* GPIO1_DEB_ENA */ +#define WM8400_GPIO1_DEB_ENA_MASK 0x0080 /* GPIO1_DEB_ENA */ +#define WM8400_GPIO1_DEB_ENA_SHIFT 7 /* GPIO1_DEB_ENA */ +#define WM8400_GPIO1_DEB_ENA_WIDTH 1 /* GPIO1_DEB_ENA */ +#define WM8400_GPIO1_IRQ_ENA 0x0040 /* GPIO1_IRQ_ENA */ +#define WM8400_GPIO1_IRQ_ENA_MASK 0x0040 /* GPIO1_IRQ_ENA */ +#define WM8400_GPIO1_IRQ_ENA_SHIFT 6 /* GPIO1_IRQ_ENA */ +#define WM8400_GPIO1_IRQ_ENA_WIDTH 1 /* GPIO1_IRQ_ENA */ +#define WM8400_GPIO1_PU 0x0020 /* GPIO1_PU */ +#define WM8400_GPIO1_PU_MASK 0x0020 /* GPIO1_PU */ +#define WM8400_GPIO1_PU_SHIFT 5 /* GPIO1_PU */ +#define WM8400_GPIO1_PU_WIDTH 1 /* GPIO1_PU */ +#define WM8400_GPIO1_PD 0x0010 /* GPIO1_PD */ +#define WM8400_GPIO1_PD_MASK 0x0010 /* GPIO1_PD */ +#define WM8400_GPIO1_PD_SHIFT 4 /* GPIO1_PD */ +#define WM8400_GPIO1_PD_WIDTH 1 /* GPIO1_PD */ +#define WM8400_GPIO1_SEL_MASK 0x000F /* GPIO1_SEL - [3:0] */ +#define WM8400_GPIO1_SEL_SHIFT 0 /* GPIO1_SEL - [3:0] */ +#define WM8400_GPIO1_SEL_WIDTH 4 /* GPIO1_SEL - [3:0] */ + +/* + * R20 (0x14) - GPIO3 & GPIO4 + */ +#define WM8400_GPIO4_DEB_ENA 0x8000 /* GPIO4_DEB_ENA */ +#define WM8400_GPIO4_DEB_ENA_MASK 0x8000 /* GPIO4_DEB_ENA */ +#define WM8400_GPIO4_DEB_ENA_SHIFT 15 /* GPIO4_DEB_ENA */ +#define WM8400_GPIO4_DEB_ENA_WIDTH 1 /* GPIO4_DEB_ENA */ +#define WM8400_GPIO4_IRQ_ENA 0x4000 /* GPIO4_IRQ_ENA */ +#define WM8400_GPIO4_IRQ_ENA_MASK 0x4000 /* GPIO4_IRQ_ENA */ +#define WM8400_GPIO4_IRQ_ENA_SHIFT 14 /* GPIO4_IRQ_ENA */ +#define WM8400_GPIO4_IRQ_ENA_WIDTH 1 /* GPIO4_IRQ_ENA */ +#define WM8400_GPIO4_PU 0x2000 /* GPIO4_PU */ +#define WM8400_GPIO4_PU_MASK 0x2000 /* GPIO4_PU */ +#define WM8400_GPIO4_PU_SHIFT 13 /* GPIO4_PU */ +#define WM8400_GPIO4_PU_WIDTH 1 /* GPIO4_PU */ +#define WM8400_GPIO4_PD 0x1000 /* GPIO4_PD */ +#define WM8400_GPIO4_PD_MASK 0x1000 /* GPIO4_PD */ +#define WM8400_GPIO4_PD_SHIFT 12 /* GPIO4_PD */ +#define WM8400_GPIO4_PD_WIDTH 1 /* GPIO4_PD */ +#define WM8400_GPIO4_SEL_MASK 0x0F00 /* GPIO4_SEL - [11:8] */ +#define WM8400_GPIO4_SEL_SHIFT 8 /* GPIO4_SEL - [11:8] */ +#define WM8400_GPIO4_SEL_WIDTH 4 /* GPIO4_SEL - [11:8] */ +#define WM8400_GPIO3_DEB_ENA 0x0080 /* GPIO3_DEB_ENA */ +#define WM8400_GPIO3_DEB_ENA_MASK 0x0080 /* GPIO3_DEB_ENA */ +#define WM8400_GPIO3_DEB_ENA_SHIFT 7 /* GPIO3_DEB_ENA */ +#define WM8400_GPIO3_DEB_ENA_WIDTH 1 /* GPIO3_DEB_ENA */ +#define WM8400_GPIO3_IRQ_ENA 0x0040 /* GPIO3_IRQ_ENA */ +#define WM8400_GPIO3_IRQ_ENA_MASK 0x0040 /* GPIO3_IRQ_ENA */ +#define WM8400_GPIO3_IRQ_ENA_SHIFT 6 /* GPIO3_IRQ_ENA */ +#define WM8400_GPIO3_IRQ_ENA_WIDTH 1 /* GPIO3_IRQ_ENA */ +#define WM8400_GPIO3_PU 0x0020 /* GPIO3_PU */ +#define WM8400_GPIO3_PU_MASK 0x0020 /* GPIO3_PU */ +#define WM8400_GPIO3_PU_SHIFT 5 /* GPIO3_PU */ +#define WM8400_GPIO3_PU_WIDTH 1 /* GPIO3_PU */ +#define WM8400_GPIO3_PD 0x0010 /* GPIO3_PD */ +#define WM8400_GPIO3_PD_MASK 0x0010 /* GPIO3_PD */ +#define WM8400_GPIO3_PD_SHIFT 4 /* GPIO3_PD */ +#define WM8400_GPIO3_PD_WIDTH 1 /* GPIO3_PD */ +#define WM8400_GPIO3_SEL_MASK 0x000F /* GPIO3_SEL - [3:0] */ +#define WM8400_GPIO3_SEL_SHIFT 0 /* GPIO3_SEL - [3:0] */ +#define WM8400_GPIO3_SEL_WIDTH 4 /* GPIO3_SEL - [3:0] */ + +/* + * R21 (0x15) - GPIO5 & GPIO6 + */ +#define WM8400_GPIO6_DEB_ENA 0x8000 /* GPIO6_DEB_ENA */ +#define WM8400_GPIO6_DEB_ENA_MASK 0x8000 /* GPIO6_DEB_ENA */ +#define WM8400_GPIO6_DEB_ENA_SHIFT 15 /* GPIO6_DEB_ENA */ +#define WM8400_GPIO6_DEB_ENA_WIDTH 1 /* GPIO6_DEB_ENA */ +#define WM8400_GPIO6_IRQ_ENA 0x4000 /* GPIO6_IRQ_ENA */ +#define WM8400_GPIO6_IRQ_ENA_MASK 0x4000 /* GPIO6_IRQ_ENA */ +#define WM8400_GPIO6_IRQ_ENA_SHIFT 14 /* GPIO6_IRQ_ENA */ +#define WM8400_GPIO6_IRQ_ENA_WIDTH 1 /* GPIO6_IRQ_ENA */ +#define WM8400_GPIO6_PU 0x2000 /* GPIO6_PU */ +#define WM8400_GPIO6_PU_MASK 0x2000 /* GPIO6_PU */ +#define WM8400_GPIO6_PU_SHIFT 13 /* GPIO6_PU */ +#define WM8400_GPIO6_PU_WIDTH 1 /* GPIO6_PU */ +#define WM8400_GPIO6_PD 0x1000 /* GPIO6_PD */ +#define WM8400_GPIO6_PD_MASK 0x1000 /* GPIO6_PD */ +#define WM8400_GPIO6_PD_SHIFT 12 /* GPIO6_PD */ +#define WM8400_GPIO6_PD_WIDTH 1 /* GPIO6_PD */ +#define WM8400_GPIO6_SEL_MASK 0x0F00 /* GPIO6_SEL - [11:8] */ +#define WM8400_GPIO6_SEL_SHIFT 8 /* GPIO6_SEL - [11:8] */ +#define WM8400_GPIO6_SEL_WIDTH 4 /* GPIO6_SEL - [11:8] */ +#define WM8400_GPIO5_DEB_ENA 0x0080 /* GPIO5_DEB_ENA */ +#define WM8400_GPIO5_DEB_ENA_MASK 0x0080 /* GPIO5_DEB_ENA */ +#define WM8400_GPIO5_DEB_ENA_SHIFT 7 /* GPIO5_DEB_ENA */ +#define WM8400_GPIO5_DEB_ENA_WIDTH 1 /* GPIO5_DEB_ENA */ +#define WM8400_GPIO5_IRQ_ENA 0x0040 /* GPIO5_IRQ_ENA */ +#define WM8400_GPIO5_IRQ_ENA_MASK 0x0040 /* GPIO5_IRQ_ENA */ +#define WM8400_GPIO5_IRQ_ENA_SHIFT 6 /* GPIO5_IRQ_ENA */ +#define WM8400_GPIO5_IRQ_ENA_WIDTH 1 /* GPIO5_IRQ_ENA */ +#define WM8400_GPIO5_PU 0x0020 /* GPIO5_PU */ +#define WM8400_GPIO5_PU_MASK 0x0020 /* GPIO5_PU */ +#define WM8400_GPIO5_PU_SHIFT 5 /* GPIO5_PU */ +#define WM8400_GPIO5_PU_WIDTH 1 /* GPIO5_PU */ +#define WM8400_GPIO5_PD 0x0010 /* GPIO5_PD */ +#define WM8400_GPIO5_PD_MASK 0x0010 /* GPIO5_PD */ +#define WM8400_GPIO5_PD_SHIFT 4 /* GPIO5_PD */ +#define WM8400_GPIO5_PD_WIDTH 1 /* GPIO5_PD */ +#define WM8400_GPIO5_SEL_MASK 0x000F /* GPIO5_SEL - [3:0] */ +#define WM8400_GPIO5_SEL_SHIFT 0 /* GPIO5_SEL - [3:0] */ +#define WM8400_GPIO5_SEL_WIDTH 4 /* GPIO5_SEL - [3:0] */ + +/* + * R22 (0x16) - GPIOCTRL 2 + */ +#define WM8400_TEMPOK_IRQ_ENA 0x0800 /* TEMPOK_IRQ_ENA */ +#define WM8400_TEMPOK_IRQ_ENA_MASK 0x0800 /* TEMPOK_IRQ_ENA */ +#define WM8400_TEMPOK_IRQ_ENA_SHIFT 11 /* TEMPOK_IRQ_ENA */ +#define WM8400_TEMPOK_IRQ_ENA_WIDTH 1 /* TEMPOK_IRQ_ENA */ +#define WM8400_MIC1SHRT_IRQ_ENA 0x0400 /* MIC1SHRT_IRQ_ENA */ +#define WM8400_MIC1SHRT_IRQ_ENA_MASK 0x0400 /* MIC1SHRT_IRQ_ENA */ +#define WM8400_MIC1SHRT_IRQ_ENA_SHIFT 10 /* MIC1SHRT_IRQ_ENA */ +#define WM8400_MIC1SHRT_IRQ_ENA_WIDTH 1 /* MIC1SHRT_IRQ_ENA */ +#define WM8400_MIC1DET_IRQ_ENA 0x0200 /* MIC1DET_IRQ_ENA */ +#define WM8400_MIC1DET_IRQ_ENA_MASK 0x0200 /* MIC1DET_IRQ_ENA */ +#define WM8400_MIC1DET_IRQ_ENA_SHIFT 9 /* MIC1DET_IRQ_ENA */ +#define WM8400_MIC1DET_IRQ_ENA_WIDTH 1 /* MIC1DET_IRQ_ENA */ +#define WM8400_FLL_LCK_IRQ_ENA 0x0100 /* FLL_LCK_IRQ_ENA */ +#define WM8400_FLL_LCK_IRQ_ENA_MASK 0x0100 /* FLL_LCK_IRQ_ENA */ +#define WM8400_FLL_LCK_IRQ_ENA_SHIFT 8 /* FLL_LCK_IRQ_ENA */ +#define WM8400_FLL_LCK_IRQ_ENA_WIDTH 1 /* FLL_LCK_IRQ_ENA */ +#define WM8400_GPI8_DEB_ENA 0x0080 /* GPI8_DEB_ENA */ +#define WM8400_GPI8_DEB_ENA_MASK 0x0080 /* GPI8_DEB_ENA */ +#define WM8400_GPI8_DEB_ENA_SHIFT 7 /* GPI8_DEB_ENA */ +#define WM8400_GPI8_DEB_ENA_WIDTH 1 /* GPI8_DEB_ENA */ +#define WM8400_GPI8_IRQ_ENA 0x0040 /* GPI8_IRQ_ENA */ +#define WM8400_GPI8_IRQ_ENA_MASK 0x0040 /* GPI8_IRQ_ENA */ +#define WM8400_GPI8_IRQ_ENA_SHIFT 6 /* GPI8_IRQ_ENA */ +#define WM8400_GPI8_IRQ_ENA_WIDTH 1 /* GPI8_IRQ_ENA */ +#define WM8400_GPI8_ENA 0x0010 /* GPI8_ENA */ +#define WM8400_GPI8_ENA_MASK 0x0010 /* GPI8_ENA */ +#define WM8400_GPI8_ENA_SHIFT 4 /* GPI8_ENA */ +#define WM8400_GPI8_ENA_WIDTH 1 /* GPI8_ENA */ +#define WM8400_GPI7_DEB_ENA 0x0008 /* GPI7_DEB_ENA */ +#define WM8400_GPI7_DEB_ENA_MASK 0x0008 /* GPI7_DEB_ENA */ +#define WM8400_GPI7_DEB_ENA_SHIFT 3 /* GPI7_DEB_ENA */ +#define WM8400_GPI7_DEB_ENA_WIDTH 1 /* GPI7_DEB_ENA */ +#define WM8400_GPI7_IRQ_ENA 0x0004 /* GPI7_IRQ_ENA */ +#define WM8400_GPI7_IRQ_ENA_MASK 0x0004 /* GPI7_IRQ_ENA */ +#define WM8400_GPI7_IRQ_ENA_SHIFT 2 /* GPI7_IRQ_ENA */ +#define WM8400_GPI7_IRQ_ENA_WIDTH 1 /* GPI7_IRQ_ENA */ +#define WM8400_GPI7_ENA 0x0001 /* GPI7_ENA */ +#define WM8400_GPI7_ENA_MASK 0x0001 /* GPI7_ENA */ +#define WM8400_GPI7_ENA_SHIFT 0 /* GPI7_ENA */ +#define WM8400_GPI7_ENA_WIDTH 1 /* GPI7_ENA */ + +/* + * R23 (0x17) - GPIO_POL + */ +#define WM8400_IRQ_INV 0x1000 /* IRQ_INV */ +#define WM8400_IRQ_INV_MASK 0x1000 /* IRQ_INV */ +#define WM8400_IRQ_INV_SHIFT 12 /* IRQ_INV */ +#define WM8400_IRQ_INV_WIDTH 1 /* IRQ_INV */ +#define WM8400_TEMPOK_POL 0x0800 /* TEMPOK_POL */ +#define WM8400_TEMPOK_POL_MASK 0x0800 /* TEMPOK_POL */ +#define WM8400_TEMPOK_POL_SHIFT 11 /* TEMPOK_POL */ +#define WM8400_TEMPOK_POL_WIDTH 1 /* TEMPOK_POL */ +#define WM8400_MIC1SHRT_POL 0x0400 /* MIC1SHRT_POL */ +#define WM8400_MIC1SHRT_POL_MASK 0x0400 /* MIC1SHRT_POL */ +#define WM8400_MIC1SHRT_POL_SHIFT 10 /* MIC1SHRT_POL */ +#define WM8400_MIC1SHRT_POL_WIDTH 1 /* MIC1SHRT_POL */ +#define WM8400_MIC1DET_POL 0x0200 /* MIC1DET_POL */ +#define WM8400_MIC1DET_POL_MASK 0x0200 /* MIC1DET_POL */ +#define WM8400_MIC1DET_POL_SHIFT 9 /* MIC1DET_POL */ +#define WM8400_MIC1DET_POL_WIDTH 1 /* MIC1DET_POL */ +#define WM8400_FLL_LCK_POL 0x0100 /* FLL_LCK_POL */ +#define WM8400_FLL_LCK_POL_MASK 0x0100 /* FLL_LCK_POL */ +#define WM8400_FLL_LCK_POL_SHIFT 8 /* FLL_LCK_POL */ +#define WM8400_FLL_LCK_POL_WIDTH 1 /* FLL_LCK_POL */ +#define WM8400_GPIO_POL_MASK 0x00FF /* GPIO_POL - [7:0] */ +#define WM8400_GPIO_POL_SHIFT 0 /* GPIO_POL - [7:0] */ +#define WM8400_GPIO_POL_WIDTH 8 /* GPIO_POL - [7:0] */ + +/* + * R65 (0x41) - LDO 1 Control + */ +#define WM8400_LDO1_ENA 0x8000 /* LDO1_ENA */ +#define WM8400_LDO1_ENA_MASK 0x8000 /* LDO1_ENA */ +#define WM8400_LDO1_ENA_SHIFT 15 /* LDO1_ENA */ +#define WM8400_LDO1_ENA_WIDTH 1 /* LDO1_ENA */ +#define WM8400_LDO1_SWI 0x4000 /* LDO1_SWI */ +#define WM8400_LDO1_SWI_MASK 0x4000 /* LDO1_SWI */ +#define WM8400_LDO1_SWI_SHIFT 14 /* LDO1_SWI */ +#define WM8400_LDO1_SWI_WIDTH 1 /* LDO1_SWI */ +#define WM8400_LDO1_OPFLT 0x1000 /* LDO1_OPFLT */ +#define WM8400_LDO1_OPFLT_MASK 0x1000 /* LDO1_OPFLT */ +#define WM8400_LDO1_OPFLT_SHIFT 12 /* LDO1_OPFLT */ +#define WM8400_LDO1_OPFLT_WIDTH 1 /* LDO1_OPFLT */ +#define WM8400_LDO1_ERRACT 0x0800 /* LDO1_ERRACT */ +#define WM8400_LDO1_ERRACT_MASK 0x0800 /* LDO1_ERRACT */ +#define WM8400_LDO1_ERRACT_SHIFT 11 /* LDO1_ERRACT */ +#define WM8400_LDO1_ERRACT_WIDTH 1 /* LDO1_ERRACT */ +#define WM8400_LDO1_HIB_MODE 0x0400 /* LDO1_HIB_MODE */ +#define WM8400_LDO1_HIB_MODE_MASK 0x0400 /* LDO1_HIB_MODE */ +#define WM8400_LDO1_HIB_MODE_SHIFT 10 /* LDO1_HIB_MODE */ +#define WM8400_LDO1_HIB_MODE_WIDTH 1 /* LDO1_HIB_MODE */ +#define WM8400_LDO1_VIMG_MASK 0x03E0 /* LDO1_VIMG - [9:5] */ +#define WM8400_LDO1_VIMG_SHIFT 5 /* LDO1_VIMG - [9:5] */ +#define WM8400_LDO1_VIMG_WIDTH 5 /* LDO1_VIMG - [9:5] */ +#define WM8400_LDO1_VSEL_MASK 0x001F /* LDO1_VSEL - [4:0] */ +#define WM8400_LDO1_VSEL_SHIFT 0 /* LDO1_VSEL - [4:0] */ +#define WM8400_LDO1_VSEL_WIDTH 5 /* LDO1_VSEL - [4:0] */ + +/* + * R66 (0x42) - LDO 2 Control + */ +#define WM8400_LDO2_ENA 0x8000 /* LDO2_ENA */ +#define WM8400_LDO2_ENA_MASK 0x8000 /* LDO2_ENA */ +#define WM8400_LDO2_ENA_SHIFT 15 /* LDO2_ENA */ +#define WM8400_LDO2_ENA_WIDTH 1 /* LDO2_ENA */ +#define WM8400_LDO2_SWI 0x4000 /* LDO2_SWI */ +#define WM8400_LDO2_SWI_MASK 0x4000 /* LDO2_SWI */ +#define WM8400_LDO2_SWI_SHIFT 14 /* LDO2_SWI */ +#define WM8400_LDO2_SWI_WIDTH 1 /* LDO2_SWI */ +#define WM8400_LDO2_OPFLT 0x1000 /* LDO2_OPFLT */ +#define WM8400_LDO2_OPFLT_MASK 0x1000 /* LDO2_OPFLT */ +#define WM8400_LDO2_OPFLT_SHIFT 12 /* LDO2_OPFLT */ +#define WM8400_LDO2_OPFLT_WIDTH 1 /* LDO2_OPFLT */ +#define WM8400_LDO2_ERRACT 0x0800 /* LDO2_ERRACT */ +#define WM8400_LDO2_ERRACT_MASK 0x0800 /* LDO2_ERRACT */ +#define WM8400_LDO2_ERRACT_SHIFT 11 /* LDO2_ERRACT */ +#define WM8400_LDO2_ERRACT_WIDTH 1 /* LDO2_ERRACT */ +#define WM8400_LDO2_HIB_MODE 0x0400 /* LDO2_HIB_MODE */ +#define WM8400_LDO2_HIB_MODE_MASK 0x0400 /* LDO2_HIB_MODE */ +#define WM8400_LDO2_HIB_MODE_SHIFT 10 /* LDO2_HIB_MODE */ +#define WM8400_LDO2_HIB_MODE_WIDTH 1 /* LDO2_HIB_MODE */ +#define WM8400_LDO2_VIMG_MASK 0x03E0 /* LDO2_VIMG - [9:5] */ +#define WM8400_LDO2_VIMG_SHIFT 5 /* LDO2_VIMG - [9:5] */ +#define WM8400_LDO2_VIMG_WIDTH 5 /* LDO2_VIMG - [9:5] */ +#define WM8400_LDO2_VSEL_MASK 0x001F /* LDO2_VSEL - [4:0] */ +#define WM8400_LDO2_VSEL_SHIFT 0 /* LDO2_VSEL - [4:0] */ +#define WM8400_LDO2_VSEL_WIDTH 5 /* LDO2_VSEL - [4:0] */ + +/* + * R67 (0x43) - LDO 3 Control + */ +#define WM8400_LDO3_ENA 0x8000 /* LDO3_ENA */ +#define WM8400_LDO3_ENA_MASK 0x8000 /* LDO3_ENA */ +#define WM8400_LDO3_ENA_SHIFT 15 /* LDO3_ENA */ +#define WM8400_LDO3_ENA_WIDTH 1 /* LDO3_ENA */ +#define WM8400_LDO3_SWI 0x4000 /* LDO3_SWI */ +#define WM8400_LDO3_SWI_MASK 0x4000 /* LDO3_SWI */ +#define WM8400_LDO3_SWI_SHIFT 14 /* LDO3_SWI */ +#define WM8400_LDO3_SWI_WIDTH 1 /* LDO3_SWI */ +#define WM8400_LDO3_OPFLT 0x1000 /* LDO3_OPFLT */ +#define WM8400_LDO3_OPFLT_MASK 0x1000 /* LDO3_OPFLT */ +#define WM8400_LDO3_OPFLT_SHIFT 12 /* LDO3_OPFLT */ +#define WM8400_LDO3_OPFLT_WIDTH 1 /* LDO3_OPFLT */ +#define WM8400_LDO3_ERRACT 0x0800 /* LDO3_ERRACT */ +#define WM8400_LDO3_ERRACT_MASK 0x0800 /* LDO3_ERRACT */ +#define WM8400_LDO3_ERRACT_SHIFT 11 /* LDO3_ERRACT */ +#define WM8400_LDO3_ERRACT_WIDTH 1 /* LDO3_ERRACT */ +#define WM8400_LDO3_HIB_MODE 0x0400 /* LDO3_HIB_MODE */ +#define WM8400_LDO3_HIB_MODE_MASK 0x0400 /* LDO3_HIB_MODE */ +#define WM8400_LDO3_HIB_MODE_SHIFT 10 /* LDO3_HIB_MODE */ +#define WM8400_LDO3_HIB_MODE_WIDTH 1 /* LDO3_HIB_MODE */ +#define WM8400_LDO3_VIMG_MASK 0x03E0 /* LDO3_VIMG - [9:5] */ +#define WM8400_LDO3_VIMG_SHIFT 5 /* LDO3_VIMG - [9:5] */ +#define WM8400_LDO3_VIMG_WIDTH 5 /* LDO3_VIMG - [9:5] */ +#define WM8400_LDO3_VSEL_MASK 0x001F /* LDO3_VSEL - [4:0] */ +#define WM8400_LDO3_VSEL_SHIFT 0 /* LDO3_VSEL - [4:0] */ +#define WM8400_LDO3_VSEL_WIDTH 5 /* LDO3_VSEL - [4:0] */ + +/* + * R68 (0x44) - LDO 4 Control + */ +#define WM8400_LDO4_ENA 0x8000 /* LDO4_ENA */ +#define WM8400_LDO4_ENA_MASK 0x8000 /* LDO4_ENA */ +#define WM8400_LDO4_ENA_SHIFT 15 /* LDO4_ENA */ +#define WM8400_LDO4_ENA_WIDTH 1 /* LDO4_ENA */ +#define WM8400_LDO4_SWI 0x4000 /* LDO4_SWI */ +#define WM8400_LDO4_SWI_MASK 0x4000 /* LDO4_SWI */ +#define WM8400_LDO4_SWI_SHIFT 14 /* LDO4_SWI */ +#define WM8400_LDO4_SWI_WIDTH 1 /* LDO4_SWI */ +#define WM8400_LDO4_OPFLT 0x1000 /* LDO4_OPFLT */ +#define WM8400_LDO4_OPFLT_MASK 0x1000 /* LDO4_OPFLT */ +#define WM8400_LDO4_OPFLT_SHIFT 12 /* LDO4_OPFLT */ +#define WM8400_LDO4_OPFLT_WIDTH 1 /* LDO4_OPFLT */ +#define WM8400_LDO4_ERRACT 0x0800 /* LDO4_ERRACT */ +#define WM8400_LDO4_ERRACT_MASK 0x0800 /* LDO4_ERRACT */ +#define WM8400_LDO4_ERRACT_SHIFT 11 /* LDO4_ERRACT */ +#define WM8400_LDO4_ERRACT_WIDTH 1 /* LDO4_ERRACT */ +#define WM8400_LDO4_HIB_MODE 0x0400 /* LDO4_HIB_MODE */ +#define WM8400_LDO4_HIB_MODE_MASK 0x0400 /* LDO4_HIB_MODE */ +#define WM8400_LDO4_HIB_MODE_SHIFT 10 /* LDO4_HIB_MODE */ +#define WM8400_LDO4_HIB_MODE_WIDTH 1 /* LDO4_HIB_MODE */ +#define WM8400_LDO4_VIMG_MASK 0x03E0 /* LDO4_VIMG - [9:5] */ +#define WM8400_LDO4_VIMG_SHIFT 5 /* LDO4_VIMG - [9:5] */ +#define WM8400_LDO4_VIMG_WIDTH 5 /* LDO4_VIMG - [9:5] */ +#define WM8400_LDO4_VSEL_MASK 0x001F /* LDO4_VSEL - [4:0] */ +#define WM8400_LDO4_VSEL_SHIFT 0 /* LDO4_VSEL - [4:0] */ +#define WM8400_LDO4_VSEL_WIDTH 5 /* LDO4_VSEL - [4:0] */ + +/* + * R70 (0x46) - DCDC1 Control 1 + */ +#define WM8400_DC1_ENA 0x8000 /* DC1_ENA */ +#define WM8400_DC1_ENA_MASK 0x8000 /* DC1_ENA */ +#define WM8400_DC1_ENA_SHIFT 15 /* DC1_ENA */ +#define WM8400_DC1_ENA_WIDTH 1 /* DC1_ENA */ +#define WM8400_DC1_ACTIVE 0x4000 /* DC1_ACTIVE */ +#define WM8400_DC1_ACTIVE_MASK 0x4000 /* DC1_ACTIVE */ +#define WM8400_DC1_ACTIVE_SHIFT 14 /* DC1_ACTIVE */ +#define WM8400_DC1_ACTIVE_WIDTH 1 /* DC1_ACTIVE */ +#define WM8400_DC1_SLEEP 0x2000 /* DC1_SLEEP */ +#define WM8400_DC1_SLEEP_MASK 0x2000 /* DC1_SLEEP */ +#define WM8400_DC1_SLEEP_SHIFT 13 /* DC1_SLEEP */ +#define WM8400_DC1_SLEEP_WIDTH 1 /* DC1_SLEEP */ +#define WM8400_DC1_OPFLT 0x1000 /* DC1_OPFLT */ +#define WM8400_DC1_OPFLT_MASK 0x1000 /* DC1_OPFLT */ +#define WM8400_DC1_OPFLT_SHIFT 12 /* DC1_OPFLT */ +#define WM8400_DC1_OPFLT_WIDTH 1 /* DC1_OPFLT */ +#define WM8400_DC1_ERRACT 0x0800 /* DC1_ERRACT */ +#define WM8400_DC1_ERRACT_MASK 0x0800 /* DC1_ERRACT */ +#define WM8400_DC1_ERRACT_SHIFT 11 /* DC1_ERRACT */ +#define WM8400_DC1_ERRACT_WIDTH 1 /* DC1_ERRACT */ +#define WM8400_DC1_HIB_MODE 0x0400 /* DC1_HIB_MODE */ +#define WM8400_DC1_HIB_MODE_MASK 0x0400 /* DC1_HIB_MODE */ +#define WM8400_DC1_HIB_MODE_SHIFT 10 /* DC1_HIB_MODE */ +#define WM8400_DC1_HIB_MODE_WIDTH 1 /* DC1_HIB_MODE */ +#define WM8400_DC1_SOFTST_MASK 0x0300 /* DC1_SOFTST - [9:8] */ +#define WM8400_DC1_SOFTST_SHIFT 8 /* DC1_SOFTST - [9:8] */ +#define WM8400_DC1_SOFTST_WIDTH 2 /* DC1_SOFTST - [9:8] */ +#define WM8400_DC1_OV_PROT 0x0080 /* DC1_OV_PROT */ +#define WM8400_DC1_OV_PROT_MASK 0x0080 /* DC1_OV_PROT */ +#define WM8400_DC1_OV_PROT_SHIFT 7 /* DC1_OV_PROT */ +#define WM8400_DC1_OV_PROT_WIDTH 1 /* DC1_OV_PROT */ +#define WM8400_DC1_VSEL_MASK 0x007F /* DC1_VSEL - [6:0] */ +#define WM8400_DC1_VSEL_SHIFT 0 /* DC1_VSEL - [6:0] */ +#define WM8400_DC1_VSEL_WIDTH 7 /* DC1_VSEL - [6:0] */ + +/* + * R71 (0x47) - DCDC1 Control 2 + */ +#define WM8400_DC1_FRC_PWM 0x2000 /* DC1_FRC_PWM */ +#define WM8400_DC1_FRC_PWM_MASK 0x2000 /* DC1_FRC_PWM */ +#define WM8400_DC1_FRC_PWM_SHIFT 13 /* DC1_FRC_PWM */ +#define WM8400_DC1_FRC_PWM_WIDTH 1 /* DC1_FRC_PWM */ +#define WM8400_DC1_STBY_LIM_MASK 0x0300 /* DC1_STBY_LIM - [9:8] */ +#define WM8400_DC1_STBY_LIM_SHIFT 8 /* DC1_STBY_LIM - [9:8] */ +#define WM8400_DC1_STBY_LIM_WIDTH 2 /* DC1_STBY_LIM - [9:8] */ +#define WM8400_DC1_ACT_LIM 0x0080 /* DC1_ACT_LIM */ +#define WM8400_DC1_ACT_LIM_MASK 0x0080 /* DC1_ACT_LIM */ +#define WM8400_DC1_ACT_LIM_SHIFT 7 /* DC1_ACT_LIM */ +#define WM8400_DC1_ACT_LIM_WIDTH 1 /* DC1_ACT_LIM */ +#define WM8400_DC1_VIMG_MASK 0x007F /* DC1_VIMG - [6:0] */ +#define WM8400_DC1_VIMG_SHIFT 0 /* DC1_VIMG - [6:0] */ +#define WM8400_DC1_VIMG_WIDTH 7 /* DC1_VIMG - [6:0] */ + +/* + * R72 (0x48) - DCDC2 Control 1 + */ +#define WM8400_DC2_ENA 0x8000 /* DC2_ENA */ +#define WM8400_DC2_ENA_MASK 0x8000 /* DC2_ENA */ +#define WM8400_DC2_ENA_SHIFT 15 /* DC2_ENA */ +#define WM8400_DC2_ENA_WIDTH 1 /* DC2_ENA */ +#define WM8400_DC2_ACTIVE 0x4000 /* DC2_ACTIVE */ +#define WM8400_DC2_ACTIVE_MASK 0x4000 /* DC2_ACTIVE */ +#define WM8400_DC2_ACTIVE_SHIFT 14 /* DC2_ACTIVE */ +#define WM8400_DC2_ACTIVE_WIDTH 1 /* DC2_ACTIVE */ +#define WM8400_DC2_SLEEP 0x2000 /* DC2_SLEEP */ +#define WM8400_DC2_SLEEP_MASK 0x2000 /* DC2_SLEEP */ +#define WM8400_DC2_SLEEP_SHIFT 13 /* DC2_SLEEP */ +#define WM8400_DC2_SLEEP_WIDTH 1 /* DC2_SLEEP */ +#define WM8400_DC2_OPFLT 0x1000 /* DC2_OPFLT */ +#define WM8400_DC2_OPFLT_MASK 0x1000 /* DC2_OPFLT */ +#define WM8400_DC2_OPFLT_SHIFT 12 /* DC2_OPFLT */ +#define WM8400_DC2_OPFLT_WIDTH 1 /* DC2_OPFLT */ +#define WM8400_DC2_ERRACT 0x0800 /* DC2_ERRACT */ +#define WM8400_DC2_ERRACT_MASK 0x0800 /* DC2_ERRACT */ +#define WM8400_DC2_ERRACT_SHIFT 11 /* DC2_ERRACT */ +#define WM8400_DC2_ERRACT_WIDTH 1 /* DC2_ERRACT */ +#define WM8400_DC2_HIB_MODE 0x0400 /* DC2_HIB_MODE */ +#define WM8400_DC2_HIB_MODE_MASK 0x0400 /* DC2_HIB_MODE */ +#define WM8400_DC2_HIB_MODE_SHIFT 10 /* DC2_HIB_MODE */ +#define WM8400_DC2_HIB_MODE_WIDTH 1 /* DC2_HIB_MODE */ +#define WM8400_DC2_SOFTST_MASK 0x0300 /* DC2_SOFTST - [9:8] */ +#define WM8400_DC2_SOFTST_SHIFT 8 /* DC2_SOFTST - [9:8] */ +#define WM8400_DC2_SOFTST_WIDTH 2 /* DC2_SOFTST - [9:8] */ +#define WM8400_DC2_OV_PROT 0x0080 /* DC2_OV_PROT */ +#define WM8400_DC2_OV_PROT_MASK 0x0080 /* DC2_OV_PROT */ +#define WM8400_DC2_OV_PROT_SHIFT 7 /* DC2_OV_PROT */ +#define WM8400_DC2_OV_PROT_WIDTH 1 /* DC2_OV_PROT */ +#define WM8400_DC2_VSEL_MASK 0x007F /* DC2_VSEL - [6:0] */ +#define WM8400_DC2_VSEL_SHIFT 0 /* DC2_VSEL - [6:0] */ +#define WM8400_DC2_VSEL_WIDTH 7 /* DC2_VSEL - [6:0] */ + +/* + * R73 (0x49) - DCDC2 Control 2 + */ +#define WM8400_DC2_FRC_PWM 0x2000 /* DC2_FRC_PWM */ +#define WM8400_DC2_FRC_PWM_MASK 0x2000 /* DC2_FRC_PWM */ +#define WM8400_DC2_FRC_PWM_SHIFT 13 /* DC2_FRC_PWM */ +#define WM8400_DC2_FRC_PWM_WIDTH 1 /* DC2_FRC_PWM */ +#define WM8400_DC2_STBY_LIM_MASK 0x0300 /* DC2_STBY_LIM - [9:8] */ +#define WM8400_DC2_STBY_LIM_SHIFT 8 /* DC2_STBY_LIM - [9:8] */ +#define WM8400_DC2_STBY_LIM_WIDTH 2 /* DC2_STBY_LIM - [9:8] */ +#define WM8400_DC2_ACT_LIM 0x0080 /* DC2_ACT_LIM */ +#define WM8400_DC2_ACT_LIM_MASK 0x0080 /* DC2_ACT_LIM */ +#define WM8400_DC2_ACT_LIM_SHIFT 7 /* DC2_ACT_LIM */ +#define WM8400_DC2_ACT_LIM_WIDTH 1 /* DC2_ACT_LIM */ +#define WM8400_DC2_VIMG_MASK 0x007F /* DC2_VIMG - [6:0] */ +#define WM8400_DC2_VIMG_SHIFT 0 /* DC2_VIMG - [6:0] */ +#define WM8400_DC2_VIMG_WIDTH 7 /* DC2_VIMG - [6:0] */ + +/* + * R75 (0x4B) - Interface + */ +#define WM8400_AUTOINC 0x0008 /* AUTOINC */ +#define WM8400_AUTOINC_MASK 0x0008 /* AUTOINC */ +#define WM8400_AUTOINC_SHIFT 3 /* AUTOINC */ +#define WM8400_AUTOINC_WIDTH 1 /* AUTOINC */ +#define WM8400_ARA_ENA 0x0004 /* ARA_ENA */ +#define WM8400_ARA_ENA_MASK 0x0004 /* ARA_ENA */ +#define WM8400_ARA_ENA_SHIFT 2 /* ARA_ENA */ +#define WM8400_ARA_ENA_WIDTH 1 /* ARA_ENA */ +#define WM8400_SPI_CFG 0x0002 /* SPI_CFG */ +#define WM8400_SPI_CFG_MASK 0x0002 /* SPI_CFG */ +#define WM8400_SPI_CFG_SHIFT 1 /* SPI_CFG */ +#define WM8400_SPI_CFG_WIDTH 1 /* SPI_CFG */ + +/* + * R76 (0x4C) - PM GENERAL + */ +#define WM8400_CODEC_SOFTST 0x8000 /* CODEC_SOFTST */ +#define WM8400_CODEC_SOFTST_MASK 0x8000 /* CODEC_SOFTST */ +#define WM8400_CODEC_SOFTST_SHIFT 15 /* CODEC_SOFTST */ +#define WM8400_CODEC_SOFTST_WIDTH 1 /* CODEC_SOFTST */ +#define WM8400_CODEC_SOFTSD 0x4000 /* CODEC_SOFTSD */ +#define WM8400_CODEC_SOFTSD_MASK 0x4000 /* CODEC_SOFTSD */ +#define WM8400_CODEC_SOFTSD_SHIFT 14 /* CODEC_SOFTSD */ +#define WM8400_CODEC_SOFTSD_WIDTH 1 /* CODEC_SOFTSD */ +#define WM8400_CHIP_SOFTSD 0x2000 /* CHIP_SOFTSD */ +#define WM8400_CHIP_SOFTSD_MASK 0x2000 /* CHIP_SOFTSD */ +#define WM8400_CHIP_SOFTSD_SHIFT 13 /* CHIP_SOFTSD */ +#define WM8400_CHIP_SOFTSD_WIDTH 1 /* CHIP_SOFTSD */ +#define WM8400_DSLEEP1_POL 0x0008 /* DSLEEP1_POL */ +#define WM8400_DSLEEP1_POL_MASK 0x0008 /* DSLEEP1_POL */ +#define WM8400_DSLEEP1_POL_SHIFT 3 /* DSLEEP1_POL */ +#define WM8400_DSLEEP1_POL_WIDTH 1 /* DSLEEP1_POL */ +#define WM8400_DSLEEP2_POL 0x0004 /* DSLEEP2_POL */ +#define WM8400_DSLEEP2_POL_MASK 0x0004 /* DSLEEP2_POL */ +#define WM8400_DSLEEP2_POL_SHIFT 2 /* DSLEEP2_POL */ +#define WM8400_DSLEEP2_POL_WIDTH 1 /* DSLEEP2_POL */ +#define WM8400_PWR_STATE_MASK 0x0003 /* PWR_STATE - [1:0] */ +#define WM8400_PWR_STATE_SHIFT 0 /* PWR_STATE - [1:0] */ +#define WM8400_PWR_STATE_WIDTH 2 /* PWR_STATE - [1:0] */ + +/* + * R78 (0x4E) - PM Shutdown Control + */ +#define WM8400_CHIP_GT150_ERRACT 0x0200 /* CHIP_GT150_ERRACT */ +#define WM8400_CHIP_GT150_ERRACT_MASK 0x0200 /* CHIP_GT150_ERRACT */ +#define WM8400_CHIP_GT150_ERRACT_SHIFT 9 /* CHIP_GT150_ERRACT */ +#define WM8400_CHIP_GT150_ERRACT_WIDTH 1 /* CHIP_GT150_ERRACT */ +#define WM8400_CHIP_GT115_ERRACT 0x0100 /* CHIP_GT115_ERRACT */ +#define WM8400_CHIP_GT115_ERRACT_MASK 0x0100 /* CHIP_GT115_ERRACT */ +#define WM8400_CHIP_GT115_ERRACT_SHIFT 8 /* CHIP_GT115_ERRACT */ +#define WM8400_CHIP_GT115_ERRACT_WIDTH 1 /* CHIP_GT115_ERRACT */ +#define WM8400_LINE_CMP_ERRACT 0x0080 /* LINE_CMP_ERRACT */ +#define WM8400_LINE_CMP_ERRACT_MASK 0x0080 /* LINE_CMP_ERRACT */ +#define WM8400_LINE_CMP_ERRACT_SHIFT 7 /* LINE_CMP_ERRACT */ +#define WM8400_LINE_CMP_ERRACT_WIDTH 1 /* LINE_CMP_ERRACT */ +#define WM8400_UVLO_ERRACT 0x0040 /* UVLO_ERRACT */ +#define WM8400_UVLO_ERRACT_MASK 0x0040 /* UVLO_ERRACT */ +#define WM8400_UVLO_ERRACT_SHIFT 6 /* UVLO_ERRACT */ +#define WM8400_UVLO_ERRACT_WIDTH 1 /* UVLO_ERRACT */ + +/* + * R79 (0x4F) - Interrupt Status 1 + */ +#define WM8400_MICD_CINT 0x8000 /* MICD_CINT */ +#define WM8400_MICD_CINT_MASK 0x8000 /* MICD_CINT */ +#define WM8400_MICD_CINT_SHIFT 15 /* MICD_CINT */ +#define WM8400_MICD_CINT_WIDTH 1 /* MICD_CINT */ +#define WM8400_MICSCD_CINT 0x4000 /* MICSCD_CINT */ +#define WM8400_MICSCD_CINT_MASK 0x4000 /* MICSCD_CINT */ +#define WM8400_MICSCD_CINT_SHIFT 14 /* MICSCD_CINT */ +#define WM8400_MICSCD_CINT_WIDTH 1 /* MICSCD_CINT */ +#define WM8400_JDL_CINT 0x2000 /* JDL_CINT */ +#define WM8400_JDL_CINT_MASK 0x2000 /* JDL_CINT */ +#define WM8400_JDL_CINT_SHIFT 13 /* JDL_CINT */ +#define WM8400_JDL_CINT_WIDTH 1 /* JDL_CINT */ +#define WM8400_JDR_CINT 0x1000 /* JDR_CINT */ +#define WM8400_JDR_CINT_MASK 0x1000 /* JDR_CINT */ +#define WM8400_JDR_CINT_SHIFT 12 /* JDR_CINT */ +#define WM8400_JDR_CINT_WIDTH 1 /* JDR_CINT */ +#define WM8400_CODEC_SEQ_END_EINT 0x0800 /* CODEC_SEQ_END_EINT */ +#define WM8400_CODEC_SEQ_END_EINT_MASK 0x0800 /* CODEC_SEQ_END_EINT */ +#define WM8400_CODEC_SEQ_END_EINT_SHIFT 11 /* CODEC_SEQ_END_EINT */ +#define WM8400_CODEC_SEQ_END_EINT_WIDTH 1 /* CODEC_SEQ_END_EINT */ +#define WM8400_CDEL_TO_EINT 0x0400 /* CDEL_TO_EINT */ +#define WM8400_CDEL_TO_EINT_MASK 0x0400 /* CDEL_TO_EINT */ +#define WM8400_CDEL_TO_EINT_SHIFT 10 /* CDEL_TO_EINT */ +#define WM8400_CDEL_TO_EINT_WIDTH 1 /* CDEL_TO_EINT */ +#define WM8400_CHIP_GT150_EINT 0x0200 /* CHIP_GT150_EINT */ +#define WM8400_CHIP_GT150_EINT_MASK 0x0200 /* CHIP_GT150_EINT */ +#define WM8400_CHIP_GT150_EINT_SHIFT 9 /* CHIP_GT150_EINT */ +#define WM8400_CHIP_GT150_EINT_WIDTH 1 /* CHIP_GT150_EINT */ +#define WM8400_CHIP_GT115_EINT 0x0100 /* CHIP_GT115_EINT */ +#define WM8400_CHIP_GT115_EINT_MASK 0x0100 /* CHIP_GT115_EINT */ +#define WM8400_CHIP_GT115_EINT_SHIFT 8 /* CHIP_GT115_EINT */ +#define WM8400_CHIP_GT115_EINT_WIDTH 1 /* CHIP_GT115_EINT */ +#define WM8400_LINE_CMP_EINT 0x0080 /* LINE_CMP_EINT */ +#define WM8400_LINE_CMP_EINT_MASK 0x0080 /* LINE_CMP_EINT */ +#define WM8400_LINE_CMP_EINT_SHIFT 7 /* LINE_CMP_EINT */ +#define WM8400_LINE_CMP_EINT_WIDTH 1 /* LINE_CMP_EINT */ +#define WM8400_UVLO_EINT 0x0040 /* UVLO_EINT */ +#define WM8400_UVLO_EINT_MASK 0x0040 /* UVLO_EINT */ +#define WM8400_UVLO_EINT_SHIFT 6 /* UVLO_EINT */ +#define WM8400_UVLO_EINT_WIDTH 1 /* UVLO_EINT */ +#define WM8400_DC2_UV_EINT 0x0020 /* DC2_UV_EINT */ +#define WM8400_DC2_UV_EINT_MASK 0x0020 /* DC2_UV_EINT */ +#define WM8400_DC2_UV_EINT_SHIFT 5 /* DC2_UV_EINT */ +#define WM8400_DC2_UV_EINT_WIDTH 1 /* DC2_UV_EINT */ +#define WM8400_DC1_UV_EINT 0x0010 /* DC1_UV_EINT */ +#define WM8400_DC1_UV_EINT_MASK 0x0010 /* DC1_UV_EINT */ +#define WM8400_DC1_UV_EINT_SHIFT 4 /* DC1_UV_EINT */ +#define WM8400_DC1_UV_EINT_WIDTH 1 /* DC1_UV_EINT */ +#define WM8400_LDO4_UV_EINT 0x0008 /* LDO4_UV_EINT */ +#define WM8400_LDO4_UV_EINT_MASK 0x0008 /* LDO4_UV_EINT */ +#define WM8400_LDO4_UV_EINT_SHIFT 3 /* LDO4_UV_EINT */ +#define WM8400_LDO4_UV_EINT_WIDTH 1 /* LDO4_UV_EINT */ +#define WM8400_LDO3_UV_EINT 0x0004 /* LDO3_UV_EINT */ +#define WM8400_LDO3_UV_EINT_MASK 0x0004 /* LDO3_UV_EINT */ +#define WM8400_LDO3_UV_EINT_SHIFT 2 /* LDO3_UV_EINT */ +#define WM8400_LDO3_UV_EINT_WIDTH 1 /* LDO3_UV_EINT */ +#define WM8400_LDO2_UV_EINT 0x0002 /* LDO2_UV_EINT */ +#define WM8400_LDO2_UV_EINT_MASK 0x0002 /* LDO2_UV_EINT */ +#define WM8400_LDO2_UV_EINT_SHIFT 1 /* LDO2_UV_EINT */ +#define WM8400_LDO2_UV_EINT_WIDTH 1 /* LDO2_UV_EINT */ +#define WM8400_LDO1_UV_EINT 0x0001 /* LDO1_UV_EINT */ +#define WM8400_LDO1_UV_EINT_MASK 0x0001 /* LDO1_UV_EINT */ +#define WM8400_LDO1_UV_EINT_SHIFT 0 /* LDO1_UV_EINT */ +#define WM8400_LDO1_UV_EINT_WIDTH 1 /* LDO1_UV_EINT */ + +/* + * R80 (0x50) - Interrupt Status 1 Mask + */ +#define WM8400_IM_MICD_CINT 0x8000 /* IM_MICD_CINT */ +#define WM8400_IM_MICD_CINT_MASK 0x8000 /* IM_MICD_CINT */ +#define WM8400_IM_MICD_CINT_SHIFT 15 /* IM_MICD_CINT */ +#define WM8400_IM_MICD_CINT_WIDTH 1 /* IM_MICD_CINT */ +#define WM8400_IM_MICSCD_CINT 0x4000 /* IM_MICSCD_CINT */ +#define WM8400_IM_MICSCD_CINT_MASK 0x4000 /* IM_MICSCD_CINT */ +#define WM8400_IM_MICSCD_CINT_SHIFT 14 /* IM_MICSCD_CINT */ +#define WM8400_IM_MICSCD_CINT_WIDTH 1 /* IM_MICSCD_CINT */ +#define WM8400_IM_JDL_CINT 0x2000 /* IM_JDL_CINT */ +#define WM8400_IM_JDL_CINT_MASK 0x2000 /* IM_JDL_CINT */ +#define WM8400_IM_JDL_CINT_SHIFT 13 /* IM_JDL_CINT */ +#define WM8400_IM_JDL_CINT_WIDTH 1 /* IM_JDL_CINT */ +#define WM8400_IM_JDR_CINT 0x1000 /* IM_JDR_CINT */ +#define WM8400_IM_JDR_CINT_MASK 0x1000 /* IM_JDR_CINT */ +#define WM8400_IM_JDR_CINT_SHIFT 12 /* IM_JDR_CINT */ +#define WM8400_IM_JDR_CINT_WIDTH 1 /* IM_JDR_CINT */ +#define WM8400_IM_CODEC_SEQ_END_EINT 0x0800 /* IM_CODEC_SEQ_END_EINT */ +#define WM8400_IM_CODEC_SEQ_END_EINT_MASK 0x0800 /* IM_CODEC_SEQ_END_EINT */ +#define WM8400_IM_CODEC_SEQ_END_EINT_SHIFT 11 /* IM_CODEC_SEQ_END_EINT */ +#define WM8400_IM_CODEC_SEQ_END_EINT_WIDTH 1 /* IM_CODEC_SEQ_END_EINT */ +#define WM8400_IM_CDEL_TO_EINT 0x0400 /* IM_CDEL_TO_EINT */ +#define WM8400_IM_CDEL_TO_EINT_MASK 0x0400 /* IM_CDEL_TO_EINT */ +#define WM8400_IM_CDEL_TO_EINT_SHIFT 10 /* IM_CDEL_TO_EINT */ +#define WM8400_IM_CDEL_TO_EINT_WIDTH 1 /* IM_CDEL_TO_EINT */ +#define WM8400_IM_CHIP_GT150_EINT 0x0200 /* IM_CHIP_GT150_EINT */ +#define WM8400_IM_CHIP_GT150_EINT_MASK 0x0200 /* IM_CHIP_GT150_EINT */ +#define WM8400_IM_CHIP_GT150_EINT_SHIFT 9 /* IM_CHIP_GT150_EINT */ +#define WM8400_IM_CHIP_GT150_EINT_WIDTH 1 /* IM_CHIP_GT150_EINT */ +#define WM8400_IM_CHIP_GT115_EINT 0x0100 /* IM_CHIP_GT115_EINT */ +#define WM8400_IM_CHIP_GT115_EINT_MASK 0x0100 /* IM_CHIP_GT115_EINT */ +#define WM8400_IM_CHIP_GT115_EINT_SHIFT 8 /* IM_CHIP_GT115_EINT */ +#define WM8400_IM_CHIP_GT115_EINT_WIDTH 1 /* IM_CHIP_GT115_EINT */ +#define WM8400_IM_LINE_CMP_EINT 0x0080 /* IM_LINE_CMP_EINT */ +#define WM8400_IM_LINE_CMP_EINT_MASK 0x0080 /* IM_LINE_CMP_EINT */ +#define WM8400_IM_LINE_CMP_EINT_SHIFT 7 /* IM_LINE_CMP_EINT */ +#define WM8400_IM_LINE_CMP_EINT_WIDTH 1 /* IM_LINE_CMP_EINT */ +#define WM8400_IM_UVLO_EINT 0x0040 /* IM_UVLO_EINT */ +#define WM8400_IM_UVLO_EINT_MASK 0x0040 /* IM_UVLO_EINT */ +#define WM8400_IM_UVLO_EINT_SHIFT 6 /* IM_UVLO_EINT */ +#define WM8400_IM_UVLO_EINT_WIDTH 1 /* IM_UVLO_EINT */ +#define WM8400_IM_DC2_UV_EINT 0x0020 /* IM_DC2_UV_EINT */ +#define WM8400_IM_DC2_UV_EINT_MASK 0x0020 /* IM_DC2_UV_EINT */ +#define WM8400_IM_DC2_UV_EINT_SHIFT 5 /* IM_DC2_UV_EINT */ +#define WM8400_IM_DC2_UV_EINT_WIDTH 1 /* IM_DC2_UV_EINT */ +#define WM8400_IM_DC1_UV_EINT 0x0010 /* IM_DC1_UV_EINT */ +#define WM8400_IM_DC1_UV_EINT_MASK 0x0010 /* IM_DC1_UV_EINT */ +#define WM8400_IM_DC1_UV_EINT_SHIFT 4 /* IM_DC1_UV_EINT */ +#define WM8400_IM_DC1_UV_EINT_WIDTH 1 /* IM_DC1_UV_EINT */ +#define WM8400_IM_LDO4_UV_EINT 0x0008 /* IM_LDO4_UV_EINT */ +#define WM8400_IM_LDO4_UV_EINT_MASK 0x0008 /* IM_LDO4_UV_EINT */ +#define WM8400_IM_LDO4_UV_EINT_SHIFT 3 /* IM_LDO4_UV_EINT */ +#define WM8400_IM_LDO4_UV_EINT_WIDTH 1 /* IM_LDO4_UV_EINT */ +#define WM8400_IM_LDO3_UV_EINT 0x0004 /* IM_LDO3_UV_EINT */ +#define WM8400_IM_LDO3_UV_EINT_MASK 0x0004 /* IM_LDO3_UV_EINT */ +#define WM8400_IM_LDO3_UV_EINT_SHIFT 2 /* IM_LDO3_UV_EINT */ +#define WM8400_IM_LDO3_UV_EINT_WIDTH 1 /* IM_LDO3_UV_EINT */ +#define WM8400_IM_LDO2_UV_EINT 0x0002 /* IM_LDO2_UV_EINT */ +#define WM8400_IM_LDO2_UV_EINT_MASK 0x0002 /* IM_LDO2_UV_EINT */ +#define WM8400_IM_LDO2_UV_EINT_SHIFT 1 /* IM_LDO2_UV_EINT */ +#define WM8400_IM_LDO2_UV_EINT_WIDTH 1 /* IM_LDO2_UV_EINT */ +#define WM8400_IM_LDO1_UV_EINT 0x0001 /* IM_LDO1_UV_EINT */ +#define WM8400_IM_LDO1_UV_EINT_MASK 0x0001 /* IM_LDO1_UV_EINT */ +#define WM8400_IM_LDO1_UV_EINT_SHIFT 0 /* IM_LDO1_UV_EINT */ +#define WM8400_IM_LDO1_UV_EINT_WIDTH 1 /* IM_LDO1_UV_EINT */ + +/* + * R81 (0x51) - Interrupt Levels + */ +#define WM8400_MICD_LVL 0x8000 /* MICD_LVL */ +#define WM8400_MICD_LVL_MASK 0x8000 /* MICD_LVL */ +#define WM8400_MICD_LVL_SHIFT 15 /* MICD_LVL */ +#define WM8400_MICD_LVL_WIDTH 1 /* MICD_LVL */ +#define WM8400_MICSCD_LVL 0x4000 /* MICSCD_LVL */ +#define WM8400_MICSCD_LVL_MASK 0x4000 /* MICSCD_LVL */ +#define WM8400_MICSCD_LVL_SHIFT 14 /* MICSCD_LVL */ +#define WM8400_MICSCD_LVL_WIDTH 1 /* MICSCD_LVL */ +#define WM8400_JDL_LVL 0x2000 /* JDL_LVL */ +#define WM8400_JDL_LVL_MASK 0x2000 /* JDL_LVL */ +#define WM8400_JDL_LVL_SHIFT 13 /* JDL_LVL */ +#define WM8400_JDL_LVL_WIDTH 1 /* JDL_LVL */ +#define WM8400_JDR_LVL 0x1000 /* JDR_LVL */ +#define WM8400_JDR_LVL_MASK 0x1000 /* JDR_LVL */ +#define WM8400_JDR_LVL_SHIFT 12 /* JDR_LVL */ +#define WM8400_JDR_LVL_WIDTH 1 /* JDR_LVL */ +#define WM8400_CODEC_SEQ_END_LVL 0x0800 /* CODEC_SEQ_END_LVL */ +#define WM8400_CODEC_SEQ_END_LVL_MASK 0x0800 /* CODEC_SEQ_END_LVL */ +#define WM8400_CODEC_SEQ_END_LVL_SHIFT 11 /* CODEC_SEQ_END_LVL */ +#define WM8400_CODEC_SEQ_END_LVL_WIDTH 1 /* CODEC_SEQ_END_LVL */ +#define WM8400_CDEL_TO_LVL 0x0400 /* CDEL_TO_LVL */ +#define WM8400_CDEL_TO_LVL_MASK 0x0400 /* CDEL_TO_LVL */ +#define WM8400_CDEL_TO_LVL_SHIFT 10 /* CDEL_TO_LVL */ +#define WM8400_CDEL_TO_LVL_WIDTH 1 /* CDEL_TO_LVL */ +#define WM8400_CHIP_GT150_LVL 0x0200 /* CHIP_GT150_LVL */ +#define WM8400_CHIP_GT150_LVL_MASK 0x0200 /* CHIP_GT150_LVL */ +#define WM8400_CHIP_GT150_LVL_SHIFT 9 /* CHIP_GT150_LVL */ +#define WM8400_CHIP_GT150_LVL_WIDTH 1 /* CHIP_GT150_LVL */ +#define WM8400_CHIP_GT115_LVL 0x0100 /* CHIP_GT115_LVL */ +#define WM8400_CHIP_GT115_LVL_MASK 0x0100 /* CHIP_GT115_LVL */ +#define WM8400_CHIP_GT115_LVL_SHIFT 8 /* CHIP_GT115_LVL */ +#define WM8400_CHIP_GT115_LVL_WIDTH 1 /* CHIP_GT115_LVL */ +#define WM8400_LINE_CMP_LVL 0x0080 /* LINE_CMP_LVL */ +#define WM8400_LINE_CMP_LVL_MASK 0x0080 /* LINE_CMP_LVL */ +#define WM8400_LINE_CMP_LVL_SHIFT 7 /* LINE_CMP_LVL */ +#define WM8400_LINE_CMP_LVL_WIDTH 1 /* LINE_CMP_LVL */ +#define WM8400_UVLO_LVL 0x0040 /* UVLO_LVL */ +#define WM8400_UVLO_LVL_MASK 0x0040 /* UVLO_LVL */ +#define WM8400_UVLO_LVL_SHIFT 6 /* UVLO_LVL */ +#define WM8400_UVLO_LVL_WIDTH 1 /* UVLO_LVL */ +#define WM8400_DC2_UV_LVL 0x0020 /* DC2_UV_LVL */ +#define WM8400_DC2_UV_LVL_MASK 0x0020 /* DC2_UV_LVL */ +#define WM8400_DC2_UV_LVL_SHIFT 5 /* DC2_UV_LVL */ +#define WM8400_DC2_UV_LVL_WIDTH 1 /* DC2_UV_LVL */ +#define WM8400_DC1_UV_LVL 0x0010 /* DC1_UV_LVL */ +#define WM8400_DC1_UV_LVL_MASK 0x0010 /* DC1_UV_LVL */ +#define WM8400_DC1_UV_LVL_SHIFT 4 /* DC1_UV_LVL */ +#define WM8400_DC1_UV_LVL_WIDTH 1 /* DC1_UV_LVL */ +#define WM8400_LDO4_UV_LVL 0x0008 /* LDO4_UV_LVL */ +#define WM8400_LDO4_UV_LVL_MASK 0x0008 /* LDO4_UV_LVL */ +#define WM8400_LDO4_UV_LVL_SHIFT 3 /* LDO4_UV_LVL */ +#define WM8400_LDO4_UV_LVL_WIDTH 1 /* LDO4_UV_LVL */ +#define WM8400_LDO3_UV_LVL 0x0004 /* LDO3_UV_LVL */ +#define WM8400_LDO3_UV_LVL_MASK 0x0004 /* LDO3_UV_LVL */ +#define WM8400_LDO3_UV_LVL_SHIFT 2 /* LDO3_UV_LVL */ +#define WM8400_LDO3_UV_LVL_WIDTH 1 /* LDO3_UV_LVL */ +#define WM8400_LDO2_UV_LVL 0x0002 /* LDO2_UV_LVL */ +#define WM8400_LDO2_UV_LVL_MASK 0x0002 /* LDO2_UV_LVL */ +#define WM8400_LDO2_UV_LVL_SHIFT 1 /* LDO2_UV_LVL */ +#define WM8400_LDO2_UV_LVL_WIDTH 1 /* LDO2_UV_LVL */ +#define WM8400_LDO1_UV_LVL 0x0001 /* LDO1_UV_LVL */ +#define WM8400_LDO1_UV_LVL_MASK 0x0001 /* LDO1_UV_LVL */ +#define WM8400_LDO1_UV_LVL_SHIFT 0 /* LDO1_UV_LVL */ +#define WM8400_LDO1_UV_LVL_WIDTH 1 /* LDO1_UV_LVL */ + +/* + * R82 (0x52) - Shutdown Reason + */ +#define WM8400_SDR_CHIP_SOFTSD 0x2000 /* SDR_CHIP_SOFTSD */ +#define WM8400_SDR_CHIP_SOFTSD_MASK 0x2000 /* SDR_CHIP_SOFTSD */ +#define WM8400_SDR_CHIP_SOFTSD_SHIFT 13 /* SDR_CHIP_SOFTSD */ +#define WM8400_SDR_CHIP_SOFTSD_WIDTH 1 /* SDR_CHIP_SOFTSD */ +#define WM8400_SDR_NPDN 0x0800 /* SDR_NPDN */ +#define WM8400_SDR_NPDN_MASK 0x0800 /* SDR_NPDN */ +#define WM8400_SDR_NPDN_SHIFT 11 /* SDR_NPDN */ +#define WM8400_SDR_NPDN_WIDTH 1 /* SDR_NPDN */ +#define WM8400_SDR_CHIP_GT150 0x0200 /* SDR_CHIP_GT150 */ +#define WM8400_SDR_CHIP_GT150_MASK 0x0200 /* SDR_CHIP_GT150 */ +#define WM8400_SDR_CHIP_GT150_SHIFT 9 /* SDR_CHIP_GT150 */ +#define WM8400_SDR_CHIP_GT150_WIDTH 1 /* SDR_CHIP_GT150 */ +#define WM8400_SDR_CHIP_GT115 0x0100 /* SDR_CHIP_GT115 */ +#define WM8400_SDR_CHIP_GT115_MASK 0x0100 /* SDR_CHIP_GT115 */ +#define WM8400_SDR_CHIP_GT115_SHIFT 8 /* SDR_CHIP_GT115 */ +#define WM8400_SDR_CHIP_GT115_WIDTH 1 /* SDR_CHIP_GT115 */ +#define WM8400_SDR_LINE_CMP 0x0080 /* SDR_LINE_CMP */ +#define WM8400_SDR_LINE_CMP_MASK 0x0080 /* SDR_LINE_CMP */ +#define WM8400_SDR_LINE_CMP_SHIFT 7 /* SDR_LINE_CMP */ +#define WM8400_SDR_LINE_CMP_WIDTH 1 /* SDR_LINE_CMP */ +#define WM8400_SDR_UVLO 0x0040 /* SDR_UVLO */ +#define WM8400_SDR_UVLO_MASK 0x0040 /* SDR_UVLO */ +#define WM8400_SDR_UVLO_SHIFT 6 /* SDR_UVLO */ +#define WM8400_SDR_UVLO_WIDTH 1 /* SDR_UVLO */ +#define WM8400_SDR_DC2_UV 0x0020 /* SDR_DC2_UV */ +#define WM8400_SDR_DC2_UV_MASK 0x0020 /* SDR_DC2_UV */ +#define WM8400_SDR_DC2_UV_SHIFT 5 /* SDR_DC2_UV */ +#define WM8400_SDR_DC2_UV_WIDTH 1 /* SDR_DC2_UV */ +#define WM8400_SDR_DC1_UV 0x0010 /* SDR_DC1_UV */ +#define WM8400_SDR_DC1_UV_MASK 0x0010 /* SDR_DC1_UV */ +#define WM8400_SDR_DC1_UV_SHIFT 4 /* SDR_DC1_UV */ +#define WM8400_SDR_DC1_UV_WIDTH 1 /* SDR_DC1_UV */ +#define WM8400_SDR_LDO4_UV 0x0008 /* SDR_LDO4_UV */ +#define WM8400_SDR_LDO4_UV_MASK 0x0008 /* SDR_LDO4_UV */ +#define WM8400_SDR_LDO4_UV_SHIFT 3 /* SDR_LDO4_UV */ +#define WM8400_SDR_LDO4_UV_WIDTH 1 /* SDR_LDO4_UV */ +#define WM8400_SDR_LDO3_UV 0x0004 /* SDR_LDO3_UV */ +#define WM8400_SDR_LDO3_UV_MASK 0x0004 /* SDR_LDO3_UV */ +#define WM8400_SDR_LDO3_UV_SHIFT 2 /* SDR_LDO3_UV */ +#define WM8400_SDR_LDO3_UV_WIDTH 1 /* SDR_LDO3_UV */ +#define WM8400_SDR_LDO2_UV 0x0002 /* SDR_LDO2_UV */ +#define WM8400_SDR_LDO2_UV_MASK 0x0002 /* SDR_LDO2_UV */ +#define WM8400_SDR_LDO2_UV_SHIFT 1 /* SDR_LDO2_UV */ +#define WM8400_SDR_LDO2_UV_WIDTH 1 /* SDR_LDO2_UV */ +#define WM8400_SDR_LDO1_UV 0x0001 /* SDR_LDO1_UV */ +#define WM8400_SDR_LDO1_UV_MASK 0x0001 /* SDR_LDO1_UV */ +#define WM8400_SDR_LDO1_UV_SHIFT 0 /* SDR_LDO1_UV */ +#define WM8400_SDR_LDO1_UV_WIDTH 1 /* SDR_LDO1_UV */ + +/* + * R84 (0x54) - Line Circuits + */ +#define WM8400_BG_LINE_COMP 0x8000 /* BG_LINE_COMP */ +#define WM8400_BG_LINE_COMP_MASK 0x8000 /* BG_LINE_COMP */ +#define WM8400_BG_LINE_COMP_SHIFT 15 /* BG_LINE_COMP */ +#define WM8400_BG_LINE_COMP_WIDTH 1 /* BG_LINE_COMP */ +#define WM8400_LINE_CMP_VTHI_MASK 0x00F0 /* LINE_CMP_VTHI - [7:4] */ +#define WM8400_LINE_CMP_VTHI_SHIFT 4 /* LINE_CMP_VTHI - [7:4] */ +#define WM8400_LINE_CMP_VTHI_WIDTH 4 /* LINE_CMP_VTHI - [7:4] */ +#define WM8400_LINE_CMP_VTHD_MASK 0x000F /* LINE_CMP_VTHD - [3:0] */ +#define WM8400_LINE_CMP_VTHD_SHIFT 0 /* LINE_CMP_VTHD - [3:0] */ +#define WM8400_LINE_CMP_VTHD_WIDTH 4 /* LINE_CMP_VTHD - [3:0] */ + +u16 wm8400_reg_read(struct wm8400 *wm8400, u8 reg); +int wm8400_block_read(struct wm8400 *wm8400, u8 reg, int count, u16 *data); +int wm8400_set_bits(struct wm8400 *wm8400, u8 reg, u16 mask, u16 val); + +#endif diff --git a/include/linux/mfd/wm8400.h b/include/linux/mfd/wm8400.h new file mode 100644 index 000000000000..b46b566ac1ac --- /dev/null +++ b/include/linux/mfd/wm8400.h @@ -0,0 +1,40 @@ +/* + * wm8400 client interface + * + * Copyright 2008 Wolfson Microelectronics plc + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#ifndef __LINUX_MFD_WM8400_H +#define __LINUX_MFD_WM8400_H + +#include <linux/regulator/machine.h> + +#define WM8400_LDO1 0 +#define WM8400_LDO2 1 +#define WM8400_LDO3 2 +#define WM8400_LDO4 3 +#define WM8400_DCDC1 4 +#define WM8400_DCDC2 5 + +struct wm8400_platform_data { + int (*platform_init)(struct device *dev); +}; + +int wm8400_register_regulator(struct device *dev, int reg, + struct regulator_init_data *initdata); + +#endif diff --git a/include/linux/mod_devicetable.h b/include/linux/mod_devicetable.h index 3481a7d5bc0a..d6a3f47e95cb 100644 --- a/include/linux/mod_devicetable.h +++ b/include/linux/mod_devicetable.h @@ -131,6 +131,16 @@ struct usb_device_id { #define USB_DEVICE_ID_MATCH_INT_SUBCLASS 0x0100 #define USB_DEVICE_ID_MATCH_INT_PROTOCOL 0x0200 +#define HID_ANY_ID (~0) + +struct hid_device_id { + __u16 bus; + __u32 vendor; + __u32 product; + kernel_ulong_t driver_data + __attribute__((aligned(sizeof(kernel_ulong_t)))); +}; + /* s390 CCW devices */ struct ccw_device_id { __u16 match_flags; /* which fields to match against */ diff --git a/include/linux/netdevice.h b/include/linux/netdevice.h index d3ea3de70a8a..64875859d654 100644 --- a/include/linux/netdevice.h +++ b/include/linux/netdevice.h @@ -11,7 +11,7 @@ * Fred N. van Kempen, <waltje@uWalt.NL.Mugnet.ORG> * Corey Minyard <wf-rch!minyard@relay.EU.net> * Donald J. Becker, <becker@cesdis.gsfc.nasa.gov> - * Alan Cox, <Alan.Cox@linux.org> + * Alan Cox, <alan@lxorguk.ukuu.org.uk> * Bjorn Ekwall. <bj0rn@blox.se> * Pekka Riikonen <priikone@poseidon.pspt.fi> * diff --git a/include/linux/nfsd/nfsd.h b/include/linux/nfsd/nfsd.h index 108f47e5fd95..21269405ffe2 100644 --- a/include/linux/nfsd/nfsd.h +++ b/include/linux/nfsd/nfsd.h @@ -38,6 +38,7 @@ #define NFSD_MAY_LOCK 32 #define NFSD_MAY_OWNER_OVERRIDE 64 #define NFSD_MAY_LOCAL_ACCESS 128 /* IRIX doing local access check on device special file*/ +#define NFSD_MAY_BYPASS_GSS_ON_ROOT 256 #define NFSD_MAY_CREATE (NFSD_MAY_EXEC|NFSD_MAY_WRITE) #define NFSD_MAY_REMOVE (NFSD_MAY_EXEC|NFSD_MAY_WRITE|NFSD_MAY_TRUNC) @@ -125,7 +126,7 @@ int nfsd_truncate(struct svc_rqst *, struct svc_fh *, __be32 nfsd_readdir(struct svc_rqst *, struct svc_fh *, loff_t *, struct readdir_cd *, filldir_t); __be32 nfsd_statfs(struct svc_rqst *, struct svc_fh *, - struct kstatfs *); + struct kstatfs *, int access); int nfsd_notify_change(struct inode *, struct iattr *); __be32 nfsd_permission(struct svc_rqst *, struct svc_export *, diff --git a/include/linux/regulator/driver.h b/include/linux/regulator/driver.h index 1d712c7172a2..e37d80561985 100644 --- a/include/linux/regulator/driver.h +++ b/include/linux/regulator/driver.h @@ -18,8 +18,8 @@ #include <linux/device.h> #include <linux/regulator/consumer.h> -struct regulator_constraints; struct regulator_dev; +struct regulator_init_data; /** * struct regulator_ops - regulator operations. @@ -51,7 +51,7 @@ struct regulator_ops { int output_uV, int load_uA); /* the operations below are for configuration of regulator state when - * it's parent PMIC enters a global STANBY/HIBERNATE state */ + * its parent PMIC enters a global STANDBY/HIBERNATE state */ /* set regulator suspend voltage */ int (*set_suspend_voltage) (struct regulator_dev *, int uV); @@ -85,15 +85,17 @@ struct regulator_desc { struct module *owner; }; - struct regulator_dev *regulator_register(struct regulator_desc *regulator_desc, - void *reg_data); + struct device *dev, void *driver_data); void regulator_unregister(struct regulator_dev *rdev); int regulator_notifier_call_chain(struct regulator_dev *rdev, unsigned long event, void *data); void *rdev_get_drvdata(struct regulator_dev *rdev); +struct device *rdev_get_dev(struct regulator_dev *rdev); int rdev_get_id(struct regulator_dev *rdev); +void *regulator_get_init_drvdata(struct regulator_init_data *reg_init_data); + #endif diff --git a/include/linux/regulator/machine.h b/include/linux/regulator/machine.h index 11e737dbfcf2..c6d69331a81e 100644 --- a/include/linux/regulator/machine.h +++ b/include/linux/regulator/machine.h @@ -89,15 +89,33 @@ struct regulation_constraints { unsigned apply_uV:1; /* apply uV constraint iff min == max */ }; -int regulator_set_supply(const char *regulator, const char *regulator_supply); +/** + * struct regulator_consumer_supply - supply -> device mapping + * + * This maps a supply name to a device. + */ +struct regulator_consumer_supply { + struct device *dev; /* consumer */ + const char *supply; /* consumer supply - e.g. "vcc" */ +}; -const char *regulator_get_supply(const char *regulator); +/** + * struct regulator_init_data - regulator platform initialisation data. + * + * Initialisation constraints, our supply and consumers supplies. + */ +struct regulator_init_data { + struct device *supply_regulator_dev; /* or NULL for LINE */ -int regulator_set_machine_constraints(const char *regulator, - struct regulation_constraints *constraints); + struct regulation_constraints constraints; -int regulator_set_device_supply(const char *regulator, struct device *dev, - const char *supply); + int num_consumer_supplies; + struct regulator_consumer_supply *consumer_supplies; + + /* optional regulator machine specific init */ + int (*regulator_init)(void *driver_data); + void *driver_data; /* core does not touch this */ +}; int regulator_suspend_prepare(suspend_state_t state); diff --git a/include/linux/sunrpc/clnt.h b/include/linux/sunrpc/clnt.h index e5bfe01ee305..6f0ee1b84a4f 100644 --- a/include/linux/sunrpc/clnt.h +++ b/include/linux/sunrpc/clnt.h @@ -104,6 +104,7 @@ struct rpc_create_args { const struct rpc_timeout *timeout; char *servername; struct rpc_program *program; + u32 prognumber; /* overrides program->number */ u32 version; rpc_authflavor_t authflavor; unsigned long flags; @@ -124,10 +125,10 @@ struct rpc_clnt *rpc_clone_client(struct rpc_clnt *); void rpc_shutdown_client(struct rpc_clnt *); void rpc_release_client(struct rpc_clnt *); -int rpcb_register(u32, u32, int, unsigned short, int *); +int rpcb_register(u32, u32, int, unsigned short); int rpcb_v4_register(const u32 program, const u32 version, const struct sockaddr *address, - const char *netid, int *result); + const char *netid); int rpcb_getport_sync(struct sockaddr_in *, u32, u32, int); void rpcb_getport_async(struct rpc_task *); diff --git a/include/linux/sunrpc/svc.h b/include/linux/sunrpc/svc.h index dc69068d94c7..3afe7fb403b2 100644 --- a/include/linux/sunrpc/svc.h +++ b/include/linux/sunrpc/svc.h @@ -66,6 +66,7 @@ struct svc_serv { struct list_head sv_tempsocks; /* all temporary sockets */ int sv_tmpcnt; /* count of temporary sockets */ struct timer_list sv_temptimer; /* timer for aging temporary sockets */ + sa_family_t sv_family; /* listener's address family */ char * sv_name; /* service name */ @@ -265,17 +266,17 @@ struct svc_rqst { /* * Rigorous type checking on sockaddr type conversions */ -static inline struct sockaddr_in *svc_addr_in(struct svc_rqst *rqst) +static inline struct sockaddr_in *svc_addr_in(const struct svc_rqst *rqst) { return (struct sockaddr_in *) &rqst->rq_addr; } -static inline struct sockaddr_in6 *svc_addr_in6(struct svc_rqst *rqst) +static inline struct sockaddr_in6 *svc_addr_in6(const struct svc_rqst *rqst) { return (struct sockaddr_in6 *) &rqst->rq_addr; } -static inline struct sockaddr *svc_addr(struct svc_rqst *rqst) +static inline struct sockaddr *svc_addr(const struct svc_rqst *rqst) { return (struct sockaddr *) &rqst->rq_addr; } @@ -381,18 +382,20 @@ struct svc_procedure { /* * Function prototypes. */ -struct svc_serv * svc_create(struct svc_program *, unsigned int, - void (*shutdown)(struct svc_serv*)); +struct svc_serv *svc_create(struct svc_program *, unsigned int, sa_family_t, + void (*shutdown)(struct svc_serv *)); struct svc_rqst *svc_prepare_thread(struct svc_serv *serv, struct svc_pool *pool); void svc_exit_thread(struct svc_rqst *); struct svc_serv * svc_create_pooled(struct svc_program *, unsigned int, - void (*shutdown)(struct svc_serv*), svc_thread_fn, - struct module *); + sa_family_t, void (*shutdown)(struct svc_serv *), + svc_thread_fn, struct module *); int svc_set_num_threads(struct svc_serv *, struct svc_pool *, int); void svc_destroy(struct svc_serv *); int svc_process(struct svc_rqst *); -int svc_register(struct svc_serv *, int, unsigned short); +int svc_register(const struct svc_serv *, const unsigned short, + const unsigned short); + void svc_wake_up(struct svc_serv *); void svc_reserve(struct svc_rqst *rqstp, int space); struct svc_pool * svc_pool_for_cpu(struct svc_serv *serv, int cpu); diff --git a/include/linux/sunrpc/svc_rdma.h b/include/linux/sunrpc/svc_rdma.h index dc05b54bd3a3..c14fe86dac59 100644 --- a/include/linux/sunrpc/svc_rdma.h +++ b/include/linux/sunrpc/svc_rdma.h @@ -72,6 +72,7 @@ extern atomic_t rdma_stat_sq_prod; */ struct svc_rdma_op_ctxt { struct svc_rdma_op_ctxt *read_hdr; + struct svc_rdma_fastreg_mr *frmr; int hdr_count; struct xdr_buf arg; struct list_head dto_q; @@ -103,16 +104,30 @@ struct svc_rdma_chunk_sge { int start; /* sge no for this chunk */ int count; /* sge count for this chunk */ }; +struct svc_rdma_fastreg_mr { + struct ib_mr *mr; + void *kva; + struct ib_fast_reg_page_list *page_list; + int page_list_len; + unsigned long access_flags; + unsigned long map_len; + enum dma_data_direction direction; + struct list_head frmr_list; +}; struct svc_rdma_req_map { + struct svc_rdma_fastreg_mr *frmr; unsigned long count; union { struct kvec sge[RPCSVC_MAXPAGES]; struct svc_rdma_chunk_sge ch[RPCSVC_MAXPAGES]; }; }; - +#define RDMACTXT_F_FAST_UNREG 1 #define RDMACTXT_F_LAST_CTXT 2 +#define SVCRDMA_DEVCAP_FAST_REG 1 /* fast mr registration */ +#define SVCRDMA_DEVCAP_READ_W_INV 2 /* read w/ invalidate */ + struct svcxprt_rdma { struct svc_xprt sc_xprt; /* SVC transport structure */ struct rdma_cm_id *sc_cm_id; /* RDMA connection id */ @@ -136,6 +151,11 @@ struct svcxprt_rdma { struct ib_cq *sc_rq_cq; struct ib_cq *sc_sq_cq; struct ib_mr *sc_phys_mr; /* MR for server memory */ + u32 sc_dev_caps; /* distilled device caps */ + u32 sc_dma_lkey; /* local dma key */ + unsigned int sc_frmr_pg_list_len; + struct list_head sc_frmr_q; + spinlock_t sc_frmr_q_lock; spinlock_t sc_lock; /* transport lock */ @@ -192,8 +212,13 @@ extern int svc_rdma_post_recv(struct svcxprt_rdma *); extern int svc_rdma_create_listen(struct svc_serv *, int, struct sockaddr *); extern struct svc_rdma_op_ctxt *svc_rdma_get_context(struct svcxprt_rdma *); extern void svc_rdma_put_context(struct svc_rdma_op_ctxt *, int); +extern void svc_rdma_unmap_dma(struct svc_rdma_op_ctxt *ctxt); extern struct svc_rdma_req_map *svc_rdma_get_req_map(void); extern void svc_rdma_put_req_map(struct svc_rdma_req_map *); +extern int svc_rdma_fastreg(struct svcxprt_rdma *, struct svc_rdma_fastreg_mr *); +extern struct svc_rdma_fastreg_mr *svc_rdma_get_frmr(struct svcxprt_rdma *); +extern void svc_rdma_put_frmr(struct svcxprt_rdma *, + struct svc_rdma_fastreg_mr *); extern void svc_sq_reap(struct svcxprt_rdma *); extern void svc_rq_reap(struct svcxprt_rdma *); extern struct svc_xprt_class svc_rdma_class; diff --git a/include/linux/sunrpc/svcsock.h b/include/linux/sunrpc/svcsock.h index 8cff696dedf5..483e10380aae 100644 --- a/include/linux/sunrpc/svcsock.h +++ b/include/linux/sunrpc/svcsock.h @@ -39,10 +39,7 @@ int svc_send(struct svc_rqst *); void svc_drop(struct svc_rqst *); void svc_sock_update_bufs(struct svc_serv *serv); int svc_sock_names(char *buf, struct svc_serv *serv, char *toclose); -int svc_addsock(struct svc_serv *serv, - int fd, - char *name_return, - int *proto); +int svc_addsock(struct svc_serv *serv, int fd, char *name_return); void svc_init_xprt_sock(void); void svc_cleanup_xprt_sock(void); diff --git a/init/do_mounts_md.c b/init/do_mounts_md.c index 693d24694a6c..48b3fadd83ed 100644 --- a/init/do_mounts_md.c +++ b/init/do_mounts_md.c @@ -12,7 +12,12 @@ * The code for that is here. */ -static int __initdata raid_noautodetect, raid_autopart; +#ifdef CONFIG_MD_AUTODETECT +static int __initdata raid_noautodetect; +#else +static int __initdata raid_noautodetect=1; +#endif +static int __initdata raid_autopart; static struct { int minor; @@ -252,6 +257,8 @@ static int __init raid_setup(char *str) if (!strncmp(str, "noautodetect", wlen)) raid_noautodetect = 1; + if (!strncmp(str, "autodetect", wlen)) + raid_noautodetect = 0; if (strncmp(str, "partitionable", wlen)==0) raid_autopart = 1; if (strncmp(str, "part", wlen)==0) @@ -264,17 +271,32 @@ static int __init raid_setup(char *str) __setup("raid=", raid_setup); __setup("md=", md_setup); +static void autodetect_raid(void) +{ + int fd; + + /* + * Since we don't want to detect and use half a raid array, we need to + * wait for the known devices to complete their probing + */ + printk(KERN_INFO "md: Waiting for all devices to be available before autodetect\n"); + printk(KERN_INFO "md: If you don't use raid, use raid=noautodetect\n"); + while (driver_probe_done() < 0) + msleep(100); + fd = sys_open("/dev/md0", 0, 0); + if (fd >= 0) { + sys_ioctl(fd, RAID_AUTORUN, raid_autopart); + sys_close(fd); + } +} + void __init md_run_setup(void) { create_dev("/dev/md0", MKDEV(MD_MAJOR, 0)); + if (raid_noautodetect) - printk(KERN_INFO "md: Skipping autodetection of RAID arrays. (raid=noautodetect)\n"); - else { - int fd = sys_open("/dev/md0", 0, 0); - if (fd >= 0) { - sys_ioctl(fd, RAID_AUTORUN, raid_autopart); - sys_close(fd); - } - } + printk(KERN_INFO "md: Skipping autodetection of RAID arrays. (raid=autodetect will force)\n"); + else + autodetect_raid(); md_setup_drive(); } diff --git a/init/main.c b/init/main.c index 3820323c4c84..27f6bf6108e9 100644 --- a/init/main.c +++ b/init/main.c @@ -708,7 +708,7 @@ int do_one_initcall(initcall_t fn) int result; if (initcall_debug) { - printk("calling %pF\n", fn); + printk("calling %pF @ %i\n", fn, task_pid_nr(current)); t0 = ktime_get(); } diff --git a/kernel/sys_ni.c b/kernel/sys_ni.c index 08d6e1bb99ac..503d8d4eb80a 100644 --- a/kernel/sys_ni.c +++ b/kernel/sys_ni.c @@ -125,6 +125,7 @@ cond_syscall(sys_vm86old); cond_syscall(sys_vm86); cond_syscall(compat_sys_ipc); cond_syscall(compat_sys_sysctl); +cond_syscall(sys_flock); /* arch-specific weak syscall entries */ cond_syscall(sys_pciconfig_read); diff --git a/kernel/sysctl.c b/kernel/sysctl.c index c468c3c6dfc5..cfc5295f1e82 100644 --- a/kernel/sysctl.c +++ b/kernel/sysctl.c @@ -96,7 +96,7 @@ static int sixty = 60; static int neg_one = -1; #endif -#ifdef CONFIG_MMU +#if defined(CONFIG_MMU) && defined(CONFIG_FILE_LOCKING) static int two = 2; #endif @@ -1248,6 +1248,7 @@ static struct ctl_table fs_table[] = { .extra1 = &minolduid, .extra2 = &maxolduid, }, +#ifdef CONFIG_FILE_LOCKING { .ctl_name = FS_LEASES, .procname = "leases-enable", @@ -1256,6 +1257,7 @@ static struct ctl_table fs_table[] = { .mode = 0644, .proc_handler = &proc_dointvec, }, +#endif #ifdef CONFIG_DNOTIFY { .ctl_name = FS_DIR_NOTIFY, @@ -1267,6 +1269,7 @@ static struct ctl_table fs_table[] = { }, #endif #ifdef CONFIG_MMU +#ifdef CONFIG_FILE_LOCKING { .ctl_name = FS_LEASE_TIME, .procname = "lease-break-time", @@ -1278,6 +1281,7 @@ static struct ctl_table fs_table[] = { .extra1 = &zero, .extra2 = &two, }, +#endif { .procname = "aio-nr", .data = &aio_nr, diff --git a/net/802/psnap.c b/net/802/psnap.c index b3cfe5a14fca..70980baeb682 100644 --- a/net/802/psnap.c +++ b/net/802/psnap.c @@ -1,7 +1,7 @@ /* * SNAP data link layer. Derived from 802.2 * - * Alan Cox <Alan.Cox@linux.org>, + * Alan Cox <alan@lxorguk.ukuu.org.uk>, * from the 802.2 layer by Greg Page. * Merged in additions from Greg Page's psnap.c. * diff --git a/net/appletalk/ddp.c b/net/appletalk/ddp.c index 0c850427a85b..d3134e7e6ee8 100644 --- a/net/appletalk/ddp.c +++ b/net/appletalk/ddp.c @@ -2,7 +2,7 @@ * DDP: An implementation of the AppleTalk DDP protocol for * Ethernet 'ELAP'. * - * Alan Cox <Alan.Cox@linux.org> + * Alan Cox <alan@lxorguk.ukuu.org.uk> * * With more than a little assistance from * @@ -1934,6 +1934,6 @@ static void __exit atalk_exit(void) module_exit(atalk_exit); MODULE_LICENSE("GPL"); -MODULE_AUTHOR("Alan Cox <Alan.Cox@linux.org>"); +MODULE_AUTHOR("Alan Cox <alan@lxorguk.ukuu.org.uk>"); MODULE_DESCRIPTION("AppleTalk 0.20\n"); MODULE_ALIAS_NETPROTO(PF_APPLETALK); diff --git a/net/bluetooth/hidp/core.c b/net/bluetooth/hidp/core.c index 96434d774c84..acdeab3d9807 100644 --- a/net/bluetooth/hidp/core.c +++ b/net/bluetooth/hidp/core.c @@ -578,7 +578,7 @@ static int hidp_session(void *arg) if (session->hid) { if (session->hid->claimed & HID_CLAIMED_INPUT) hidinput_disconnect(session->hid); - hid_free_device(session->hid); + hid_destroy_device(session->hid); } /* Wakeup user-space polling for socket errors */ @@ -623,9 +623,15 @@ static struct device *hidp_get_device(struct hidp_session *session) static int hidp_setup_input(struct hidp_session *session, struct hidp_connadd_req *req) { - struct input_dev *input = session->input; + struct input_dev *input; int i; + input = input_allocate_device(); + if (!input) + return -ENOMEM; + + session->input = input; + input_set_drvdata(input, session); input->name = "Bluetooth HID Boot Protocol Device"; @@ -677,67 +683,114 @@ static void hidp_close(struct hid_device *hid) { } -static const struct { - __u16 idVendor; - __u16 idProduct; - unsigned quirks; -} hidp_blacklist[] = { - /* Apple wireless Mighty Mouse */ - { 0x05ac, 0x030c, HID_QUIRK_MIGHTYMOUSE | HID_QUIRK_INVERT_HWHEEL }, +static int hidp_parse(struct hid_device *hid) +{ + struct hidp_session *session = hid->driver_data; + struct hidp_connadd_req *req = session->req; + unsigned char *buf; + int ret; - { } /* Terminating entry */ -}; + buf = kmalloc(req->rd_size, GFP_KERNEL); + if (!buf) + return -ENOMEM; + + if (copy_from_user(buf, req->rd_data, req->rd_size)) { + kfree(buf); + return -EFAULT; + } + + ret = hid_parse_report(session->hid, buf, req->rd_size); + + kfree(buf); + + if (ret) + return ret; + + session->req = NULL; + + return 0; +} + +static int hidp_start(struct hid_device *hid) +{ + struct hidp_session *session = hid->driver_data; + struct hid_report *report; -static void hidp_setup_quirks(struct hid_device *hid) + list_for_each_entry(report, &hid->report_enum[HID_INPUT_REPORT]. + report_list, list) + hidp_send_report(session, report); + + list_for_each_entry(report, &hid->report_enum[HID_FEATURE_REPORT]. + report_list, list) + hidp_send_report(session, report); + + return 0; +} + +static void hidp_stop(struct hid_device *hid) { - unsigned int n; + struct hidp_session *session = hid->driver_data; + + skb_queue_purge(&session->ctrl_transmit); + skb_queue_purge(&session->intr_transmit); - for (n = 0; hidp_blacklist[n].idVendor; n++) - if (hidp_blacklist[n].idVendor == le16_to_cpu(hid->vendor) && - hidp_blacklist[n].idProduct == le16_to_cpu(hid->product)) - hid->quirks = hidp_blacklist[n].quirks; + if (hid->claimed & HID_CLAIMED_INPUT) + hidinput_disconnect(hid); + hid->claimed = 0; } -static void hidp_setup_hid(struct hidp_session *session, +static struct hid_ll_driver hidp_hid_driver = { + .parse = hidp_parse, + .start = hidp_start, + .stop = hidp_stop, + .open = hidp_open, + .close = hidp_close, + .hidinput_input_event = hidp_hidinput_event, +}; + +static int hidp_setup_hid(struct hidp_session *session, struct hidp_connadd_req *req) { - struct hid_device *hid = session->hid; - struct hid_report *report; + struct hid_device *hid; bdaddr_t src, dst; + int ret; - baswap(&src, &bt_sk(session->ctrl_sock->sk)->src); - baswap(&dst, &bt_sk(session->ctrl_sock->sk)->dst); + hid = hid_allocate_device(); + if (IS_ERR(hid)) { + ret = PTR_ERR(session->hid); + goto err; + } + session->hid = hid; + session->req = req; hid->driver_data = session; - hid->country = req->country; + baswap(&src, &bt_sk(session->ctrl_sock->sk)->src); + baswap(&dst, &bt_sk(session->ctrl_sock->sk)->dst); hid->bus = BUS_BLUETOOTH; hid->vendor = req->vendor; hid->product = req->product; hid->version = req->version; + hid->country = req->country; strncpy(hid->name, req->name, 128); strncpy(hid->phys, batostr(&src), 64); strncpy(hid->uniq, batostr(&dst), 64); - hid->dev = hidp_get_device(session); - - hid->hid_open = hidp_open; - hid->hid_close = hidp_close; - - hid->hidinput_input_event = hidp_hidinput_event; + hid->dev.parent = hidp_get_device(session); + hid->ll_driver = &hidp_hid_driver; - hidp_setup_quirks(hid); + ret = hid_add_device(hid); + if (ret) + goto err_hid; - list_for_each_entry(report, &hid->report_enum[HID_INPUT_REPORT].report_list, list) - hidp_send_report(session, report); - - list_for_each_entry(report, &hid->report_enum[HID_FEATURE_REPORT].report_list, list) - hidp_send_report(session, report); - - if (hidinput_connect(hid) == 0) - hid->claimed |= HID_CLAIMED_INPUT; + return 0; +err_hid: + hid_destroy_device(hid); + session->hid = NULL; +err: + return ret; } int hidp_add_connection(struct hidp_connadd_req *req, struct socket *ctrl_sock, struct socket *intr_sock) @@ -757,38 +810,6 @@ int hidp_add_connection(struct hidp_connadd_req *req, struct socket *ctrl_sock, BT_DBG("rd_data %p rd_size %d", req->rd_data, req->rd_size); - if (req->rd_size > 0) { - unsigned char *buf = kmalloc(req->rd_size, GFP_KERNEL); - - if (!buf) { - kfree(session); - return -ENOMEM; - } - - if (copy_from_user(buf, req->rd_data, req->rd_size)) { - kfree(buf); - kfree(session); - return -EFAULT; - } - - session->hid = hid_parse_report(buf, req->rd_size); - - kfree(buf); - - if (!session->hid) { - kfree(session); - return -EINVAL; - } - } - - if (!session->hid) { - session->input = input_allocate_device(); - if (!session->input) { - kfree(session); - return -ENOMEM; - } - } - down_write(&hidp_session_sem); s = __hidp_get_session(&bt_sk(ctrl_sock->sk)->dst); @@ -816,15 +837,18 @@ int hidp_add_connection(struct hidp_connadd_req *req, struct socket *ctrl_sock, session->flags = req->flags & (1 << HIDP_BLUETOOTH_VENDOR_ID); session->idle_to = req->idle_to; - if (session->input) { + if (req->rd_size > 0) { + err = hidp_setup_hid(session, req); + if (err && err != -ENODEV) + goto err_skb; + } + + if (!session->hid) { err = hidp_setup_input(session, req); if (err < 0) - goto failed; + goto err_skb; } - if (session->hid) - hidp_setup_hid(session, req); - __hidp_link_session(session); hidp_set_timer(session); @@ -850,17 +874,16 @@ unlink: __hidp_unlink_session(session); - if (session->input) { + if (session->input) input_unregister_device(session->input); - session->input = NULL; /* don't try to free it here */ - } - + if (session->hid) + hid_destroy_device(session->hid); +err_skb: + skb_queue_purge(&session->ctrl_transmit); + skb_queue_purge(&session->intr_transmit); failed: up_write(&hidp_session_sem); - if (session->hid) - hid_free_device(session->hid); - input_free_device(session->input); kfree(session); return err; @@ -950,18 +973,43 @@ int hidp_get_conninfo(struct hidp_conninfo *ci) return err; } +static const struct hid_device_id hidp_table[] = { + { HID_BLUETOOTH_DEVICE(HID_ANY_ID, HID_ANY_ID) }, + { } +}; + +static struct hid_driver hidp_driver = { + .name = "generic-bluetooth", + .id_table = hidp_table, +}; + static int __init hidp_init(void) { + int ret; + l2cap_load(); BT_INFO("HIDP (Human Interface Emulation) ver %s", VERSION); - return hidp_init_sockets(); + ret = hid_register_driver(&hidp_driver); + if (ret) + goto err; + + ret = hidp_init_sockets(); + if (ret) + goto err_drv; + + return 0; +err_drv: + hid_unregister_driver(&hidp_driver); +err: + return ret; } static void __exit hidp_exit(void) { hidp_cleanup_sockets(); + hid_unregister_driver(&hidp_driver); } module_init(hidp_init); diff --git a/net/bluetooth/hidp/hidp.h b/net/bluetooth/hidp/hidp.h index 343fb0566b3e..e503c89057ad 100644 --- a/net/bluetooth/hidp/hidp.h +++ b/net/bluetooth/hidp/hidp.h @@ -151,6 +151,8 @@ struct hidp_session { struct sk_buff_head ctrl_transmit; struct sk_buff_head intr_transmit; + + struct hidp_connadd_req *req; }; static inline void hidp_schedule(struct hidp_session *session) diff --git a/net/core/datagram.c b/net/core/datagram.c index 52f577a0f544..ee631843c2f5 100644 --- a/net/core/datagram.c +++ b/net/core/datagram.c @@ -9,7 +9,7 @@ * identical recvmsg() code. So we share it here. The poll was * shared before but buried in udp.c so I moved it. * - * Authors: Alan Cox <alan@redhat.com>. (datagram_poll() from old + * Authors: Alan Cox <alan@lxorguk.ukuu.org.uk>. (datagram_poll() from old * udp.c code) * * Fixes: diff --git a/net/core/dev_mcast.c b/net/core/dev_mcast.c index 5402b3b38e0d..9e2fa39f22a3 100644 --- a/net/core/dev_mcast.c +++ b/net/core/dev_mcast.c @@ -6,7 +6,7 @@ * Richard Underwood <richard@wuzz.demon.co.uk> * * Stir fried together from the IP multicast and CAP patches above - * Alan Cox <Alan.Cox@linux.org> + * Alan Cox <alan@lxorguk.ukuu.org.uk> * * Fixes: * Alan Cox : Update the device on a real delete diff --git a/net/core/pktgen.c b/net/core/pktgen.c index a756847e3814..99f656d35b4f 100644 --- a/net/core/pktgen.c +++ b/net/core/pktgen.c @@ -2474,7 +2474,7 @@ static inline int process_ipsec(struct pktgen_dev *pkt_dev, if (ret < 0) { printk(KERN_ERR "Error expanding " "ipsec packet %d\n",ret); - return 0; + goto err; } } @@ -2484,8 +2484,7 @@ static inline int process_ipsec(struct pktgen_dev *pkt_dev, if (ret) { printk(KERN_ERR "Error creating ipsec " "packet %d\n",ret); - kfree_skb(skb); - return 0; + goto err; } /* restore ll */ eth = (__u8 *) skb_push(skb, ETH_HLEN); @@ -2494,6 +2493,9 @@ static inline int process_ipsec(struct pktgen_dev *pkt_dev, } } return 1; +err: + kfree_skb(skb); + return 0; } #endif diff --git a/net/core/skbuff.c b/net/core/skbuff.c index 7f7bb1a636d9..4e22e3a35359 100644 --- a/net/core/skbuff.c +++ b/net/core/skbuff.c @@ -1,7 +1,7 @@ /* * Routines having to do with the 'struct sk_buff' memory handlers. * - * Authors: Alan Cox <iiitac@pyr.swan.ac.uk> + * Authors: Alan Cox <alan@lxorguk.ukuu.org.uk> * Florian La Roche <rzsfl@rz.uni-sb.de> * * Fixes: diff --git a/net/core/stream.c b/net/core/stream.c index a6b3437ff082..8727cead64ad 100644 --- a/net/core/stream.c +++ b/net/core/stream.c @@ -9,7 +9,7 @@ * * Authors: Arnaldo Carvalho de Melo <acme@conectiva.com.br> * (from old tcp.c code) - * Alan Cox <alan@redhat.com> (Borrowed comments 8-)) + * Alan Cox <alan@lxorguk.ukuu.org.uk> (Borrowed comments 8-)) */ #include <linux/module.h> diff --git a/net/dsa/Kconfig b/net/dsa/Kconfig index cdce4c6c672a..49211b35725b 100644 --- a/net/dsa/Kconfig +++ b/net/dsa/Kconfig @@ -1,7 +1,7 @@ menuconfig NET_DSA bool "Distributed Switch Architecture support" default n - depends on EXPERIMENTAL + depends on EXPERIMENTAL && !S390 select PHYLIB ---help--- This allows you to use hardware switch chips that use diff --git a/net/ipv4/icmp.c b/net/ipv4/icmp.c index 55c355e63234..72b2de76f1cd 100644 --- a/net/ipv4/icmp.c +++ b/net/ipv4/icmp.c @@ -1,7 +1,7 @@ /* * NET3: Implementation of the ICMP protocol layer. * - * Alan Cox, <alan@redhat.com> + * Alan Cox, <alan@lxorguk.ukuu.org.uk> * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License diff --git a/net/ipv4/igmp.c b/net/ipv4/igmp.c index 7f9e337e3908..a0d86455c53e 100644 --- a/net/ipv4/igmp.c +++ b/net/ipv4/igmp.c @@ -9,7 +9,7 @@ * seems to fall out with gcc 2.6.2. * * Authors: - * Alan Cox <Alan.Cox@linux.org> + * Alan Cox <alan@lxorguk.ukuu.org.uk> * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License diff --git a/net/ipv4/ip_fragment.c b/net/ipv4/ip_fragment.c index 2152d222b954..e4f81f54befe 100644 --- a/net/ipv4/ip_fragment.c +++ b/net/ipv4/ip_fragment.c @@ -6,7 +6,7 @@ * The IP fragmentation functionality. * * Authors: Fred N. van Kempen <waltje@uWalt.NL.Mugnet.ORG> - * Alan Cox <Alan.Cox@linux.org> + * Alan Cox <alan@lxorguk.ukuu.org.uk> * * Fixes: * Alan Cox : Split from ip.c , see ip_input.c for history. diff --git a/net/ipv4/ip_input.c b/net/ipv4/ip_input.c index e0bed56c51f1..861978a4f1a8 100644 --- a/net/ipv4/ip_input.c +++ b/net/ipv4/ip_input.c @@ -8,7 +8,7 @@ * Authors: Ross Biro * Fred N. van Kempen, <waltje@uWalt.NL.Mugnet.ORG> * Donald Becker, <becker@super.org> - * Alan Cox, <Alan.Cox@linux.org> + * Alan Cox, <alan@lxorguk.ukuu.org.uk> * Richard Underwood * Stefan Becker, <stefanb@yello.ping.de> * Jorge Cwik, <jorge@laser.satlink.net> diff --git a/net/ipv4/ipip.c b/net/ipv4/ipip.c index 4c6d2caf9203..29609d29df76 100644 --- a/net/ipv4/ipip.c +++ b/net/ipv4/ipip.c @@ -41,7 +41,7 @@ Made the tunnels use dev->name not tunnel: when error reporting. Added tx_dropped stat - -Alan Cox (Alan.Cox@linux.org) 21 March 95 + -Alan Cox (alan@lxorguk.ukuu.org.uk) 21 March 95 Reworked: Changed to tunnel to destination gateway in addition to the diff --git a/net/ipv4/ipmr.c b/net/ipv4/ipmr.c index c519b8d30eee..b42e082cc170 100644 --- a/net/ipv4/ipmr.c +++ b/net/ipv4/ipmr.c @@ -1,7 +1,7 @@ /* * IP multicast routing support for mrouted 3.6/3.8 * - * (c) 1995 Alan Cox, <alan@redhat.com> + * (c) 1995 Alan Cox, <alan@lxorguk.ukuu.org.uk> * Linux Consultancy and Custom Driver Development * * This program is free software; you can redistribute it and/or diff --git a/net/ipv4/udp.c b/net/ipv4/udp.c index eacf4cfef146..2095abc3caba 100644 --- a/net/ipv4/udp.c +++ b/net/ipv4/udp.c @@ -8,7 +8,7 @@ * Authors: Ross Biro * Fred N. van Kempen, <waltje@uWalt.NL.Mugnet.ORG> * Arnt Gulbrandsen, <agulbra@nvg.unit.no> - * Alan Cox, <Alan.Cox@linux.org> + * Alan Cox, <alan@lxorguk.ukuu.org.uk> * Hirokazu Takahashi, <taka@valinux.co.jp> * * Fixes: diff --git a/net/ipv6/af_inet6.c b/net/ipv6/af_inet6.c index 050e14b7f701..01edac888510 100644 --- a/net/ipv6/af_inet6.c +++ b/net/ipv6/af_inet6.c @@ -834,7 +834,7 @@ static void __net_exit ipv6_cleanup_mibs(struct net *net) snmp_mib_free((void **)net->mib.icmpv6msg_statistics); } -static int inet6_net_init(struct net *net) +static int __net_init inet6_net_init(struct net *net) { int err = 0; diff --git a/net/netlink/af_netlink.c b/net/netlink/af_netlink.c index b0eacc0007cc..2fd8afac5f71 100644 --- a/net/netlink/af_netlink.c +++ b/net/netlink/af_netlink.c @@ -1,7 +1,7 @@ /* * NETLINK Kernel-user communication protocol. * - * Authors: Alan Cox <alan@redhat.com> + * Authors: Alan Cox <alan@lxorguk.ukuu.org.uk> * Alexey Kuznetsov <kuznet@ms2.inr.ac.ru> * * This program is free software; you can redistribute it and/or diff --git a/net/rfkill/rfkill-input.c b/net/rfkill/rfkill-input.c index e5b69556bb5b..21124ec0a73d 100644 --- a/net/rfkill/rfkill-input.c +++ b/net/rfkill/rfkill-input.c @@ -16,6 +16,7 @@ #include <linux/workqueue.h> #include <linux/init.h> #include <linux/rfkill.h> +#include <linux/sched.h> #include "rfkill-input.h" diff --git a/net/sunrpc/clnt.c b/net/sunrpc/clnt.c index 76739e928d0d..da0789fa1b88 100644 --- a/net/sunrpc/clnt.c +++ b/net/sunrpc/clnt.c @@ -174,7 +174,7 @@ static struct rpc_clnt * rpc_new_client(const struct rpc_create_args *args, stru clnt->cl_procinfo = version->procs; clnt->cl_maxproc = version->nrprocs; clnt->cl_protname = program->name; - clnt->cl_prog = program->number; + clnt->cl_prog = args->prognumber ? : program->number; clnt->cl_vers = version->number; clnt->cl_stats = program->stats; clnt->cl_metrics = rpc_alloc_iostats(clnt); diff --git a/net/sunrpc/rpcb_clnt.c b/net/sunrpc/rpcb_clnt.c index 24db2b4d12d3..34abc91058d8 100644 --- a/net/sunrpc/rpcb_clnt.c +++ b/net/sunrpc/rpcb_clnt.c @@ -20,6 +20,7 @@ #include <linux/in6.h> #include <linux/kernel.h> #include <linux/errno.h> +#include <net/ipv6.h> #include <linux/sunrpc/clnt.h> #include <linux/sunrpc/sched.h> @@ -176,13 +177,12 @@ static struct rpc_clnt *rpcb_create(char *hostname, struct sockaddr *srvaddr, } static int rpcb_register_call(struct sockaddr *addr, size_t addrlen, - u32 version, struct rpc_message *msg, - int *result) + u32 version, struct rpc_message *msg) { struct rpc_clnt *rpcb_clnt; - int error = 0; + int result, error = 0; - *result = 0; + msg->rpc_resp = &result; rpcb_clnt = rpcb_create_local(addr, addrlen, version); if (!IS_ERR(rpcb_clnt)) { @@ -191,12 +191,15 @@ static int rpcb_register_call(struct sockaddr *addr, size_t addrlen, } else error = PTR_ERR(rpcb_clnt); - if (error < 0) + if (error < 0) { printk(KERN_WARNING "RPC: failed to contact local rpcbind " "server (errno %d).\n", -error); - dprintk("RPC: registration status %d/%d\n", error, *result); + return error; + } - return error; + if (!result) + return -EACCES; + return 0; } /** @@ -205,7 +208,11 @@ static int rpcb_register_call(struct sockaddr *addr, size_t addrlen, * @vers: RPC version number to bind * @prot: transport protocol to register * @port: port value to register - * @okay: OUT: result code + * + * Returns zero if the registration request was dispatched successfully + * and the rpcbind daemon returned success. Otherwise, returns an errno + * value that reflects the nature of the error (request could not be + * dispatched, timed out, or rpcbind returned an error). * * RPC services invoke this function to advertise their contact * information via the system's rpcbind daemon. RPC services @@ -217,15 +224,6 @@ static int rpcb_register_call(struct sockaddr *addr, size_t addrlen, * all registered transports for [program, version] from the local * rpcbind database. * - * Returns zero if the registration request was dispatched - * successfully and a reply was received. The rpcbind daemon's - * boolean result code is stored in *okay. - * - * Returns an errno value and sets *result to zero if there was - * some problem that prevented the rpcbind request from being - * dispatched, or if the rpcbind daemon did not respond within - * the timeout. - * * This function uses rpcbind protocol version 2 to contact the * local rpcbind daemon. * @@ -236,7 +234,7 @@ static int rpcb_register_call(struct sockaddr *addr, size_t addrlen, * IN6ADDR_ANY (ie available for all AF_INET and AF_INET6 * addresses). */ -int rpcb_register(u32 prog, u32 vers, int prot, unsigned short port, int *okay) +int rpcb_register(u32 prog, u32 vers, int prot, unsigned short port) { struct rpcbind_args map = { .r_prog = prog, @@ -246,7 +244,6 @@ int rpcb_register(u32 prog, u32 vers, int prot, unsigned short port, int *okay) }; struct rpc_message msg = { .rpc_argp = &map, - .rpc_resp = okay, }; dprintk("RPC: %sregistering (%u, %u, %d, %u) with local " @@ -259,7 +256,7 @@ int rpcb_register(u32 prog, u32 vers, int prot, unsigned short port, int *okay) return rpcb_register_call((struct sockaddr *)&rpcb_inaddr_loopback, sizeof(rpcb_inaddr_loopback), - RPCBVERS_2, &msg, okay); + RPCBVERS_2, &msg); } /* @@ -290,7 +287,7 @@ static int rpcb_register_netid4(struct sockaddr_in *address_to_register, return rpcb_register_call((struct sockaddr *)&rpcb_inaddr_loopback, sizeof(rpcb_inaddr_loopback), - RPCBVERS_4, msg, msg->rpc_resp); + RPCBVERS_4, msg); } /* @@ -304,10 +301,13 @@ static int rpcb_register_netid6(struct sockaddr_in6 *address_to_register, char buf[64]; /* Construct AF_INET6 universal address */ - snprintf(buf, sizeof(buf), - NIP6_FMT".%u.%u", - NIP6(address_to_register->sin6_addr), - port >> 8, port & 0xff); + if (ipv6_addr_any(&address_to_register->sin6_addr)) + snprintf(buf, sizeof(buf), "::.%u.%u", + port >> 8, port & 0xff); + else + snprintf(buf, sizeof(buf), NIP6_FMT".%u.%u", + NIP6(address_to_register->sin6_addr), + port >> 8, port & 0xff); map->r_addr = buf; dprintk("RPC: %sregistering [%u, %u, %s, '%s'] with " @@ -321,7 +321,7 @@ static int rpcb_register_netid6(struct sockaddr_in6 *address_to_register, return rpcb_register_call((struct sockaddr *)&rpcb_in6addr_loopback, sizeof(rpcb_in6addr_loopback), - RPCBVERS_4, msg, msg->rpc_resp); + RPCBVERS_4, msg); } /** @@ -330,7 +330,11 @@ static int rpcb_register_netid6(struct sockaddr_in6 *address_to_register, * @version: RPC version number of service to (un)register * @address: address family, IP address, and port to (un)register * @netid: netid of transport protocol to (un)register - * @result: result code from rpcbind RPC call + * + * Returns zero if the registration request was dispatched successfully + * and the rpcbind daemon returned success. Otherwise, returns an errno + * value that reflects the nature of the error (request could not be + * dispatched, timed out, or rpcbind returned an error). * * RPC services invoke this function to advertise their contact * information via the system's rpcbind daemon. RPC services @@ -342,15 +346,6 @@ static int rpcb_register_netid6(struct sockaddr_in6 *address_to_register, * to zero. Callers pass a netid of "" to unregister all * transport netids associated with [program, version, address]. * - * Returns zero if the registration request was dispatched - * successfully and a reply was received. The rpcbind daemon's - * result code is stored in *result. - * - * Returns an errno value and sets *result to zero if there was - * some problem that prevented the rpcbind request from being - * dispatched, or if the rpcbind daemon did not respond within - * the timeout. - * * This function uses rpcbind protocol version 4 to contact the * local rpcbind daemon. The local rpcbind daemon must support * version 4 of the rpcbind protocol in order for these functions @@ -372,8 +367,7 @@ static int rpcb_register_netid6(struct sockaddr_in6 *address_to_register, * advertises the service on all IPv4 and IPv6 addresses. */ int rpcb_v4_register(const u32 program, const u32 version, - const struct sockaddr *address, const char *netid, - int *result) + const struct sockaddr *address, const char *netid) { struct rpcbind_args map = { .r_prog = program, @@ -383,11 +377,8 @@ int rpcb_v4_register(const u32 program, const u32 version, }; struct rpc_message msg = { .rpc_argp = &map, - .rpc_resp = result, }; - *result = 0; - switch (address->sa_family) { case AF_INET: return rpcb_register_netid4((struct sockaddr_in *)address, @@ -633,7 +624,7 @@ static void rpcb_getport_done(struct rpc_task *child, void *data) static int rpcb_encode_mapping(struct rpc_rqst *req, __be32 *p, struct rpcbind_args *rpcb) { - dprintk("RPC: rpcb_encode_mapping(%u, %u, %d, %u)\n", + dprintk("RPC: encoding rpcb request (%u, %u, %d, %u)\n", rpcb->r_prog, rpcb->r_vers, rpcb->r_prot, rpcb->r_port); *p++ = htonl(rpcb->r_prog); *p++ = htonl(rpcb->r_vers); @@ -648,7 +639,7 @@ static int rpcb_decode_getport(struct rpc_rqst *req, __be32 *p, unsigned short *portp) { *portp = (unsigned short) ntohl(*p++); - dprintk("RPC: rpcb_decode_getport result %u\n", + dprintk("RPC: rpcb getport result: %u\n", *portp); return 0; } @@ -657,7 +648,7 @@ static int rpcb_decode_set(struct rpc_rqst *req, __be32 *p, unsigned int *boolp) { *boolp = (unsigned int) ntohl(*p++); - dprintk("RPC: rpcb_decode_set: call %s\n", + dprintk("RPC: rpcb set/unset call %s\n", (*boolp ? "succeeded" : "failed")); return 0; } @@ -665,7 +656,7 @@ static int rpcb_decode_set(struct rpc_rqst *req, __be32 *p, static int rpcb_encode_getaddr(struct rpc_rqst *req, __be32 *p, struct rpcbind_args *rpcb) { - dprintk("RPC: rpcb_encode_getaddr(%u, %u, %s)\n", + dprintk("RPC: encoding rpcb request (%u, %u, %s)\n", rpcb->r_prog, rpcb->r_vers, rpcb->r_addr); *p++ = htonl(rpcb->r_prog); *p++ = htonl(rpcb->r_vers); diff --git a/net/sunrpc/svc.c b/net/sunrpc/svc.c index 5a32cb7c4bb4..54c98d876847 100644 --- a/net/sunrpc/svc.c +++ b/net/sunrpc/svc.c @@ -28,6 +28,8 @@ #define RPCDBG_FACILITY RPCDBG_SVCDSP +static void svc_unregister(const struct svc_serv *serv); + #define svc_serv_is_pooled(serv) ((serv)->sv_function) /* @@ -357,7 +359,7 @@ svc_pool_for_cpu(struct svc_serv *serv, int cpu) */ static struct svc_serv * __svc_create(struct svc_program *prog, unsigned int bufsize, int npools, - void (*shutdown)(struct svc_serv *serv)) + sa_family_t family, void (*shutdown)(struct svc_serv *serv)) { struct svc_serv *serv; unsigned int vers; @@ -366,6 +368,7 @@ __svc_create(struct svc_program *prog, unsigned int bufsize, int npools, if (!(serv = kzalloc(sizeof(*serv), GFP_KERNEL))) return NULL; + serv->sv_family = family; serv->sv_name = prog->pg_name; serv->sv_program = prog; serv->sv_nrthreads = 1; @@ -416,30 +419,29 @@ __svc_create(struct svc_program *prog, unsigned int bufsize, int npools, spin_lock_init(&pool->sp_lock); } - /* Remove any stale portmap registrations */ - svc_register(serv, 0, 0); + svc_unregister(serv); return serv; } struct svc_serv * svc_create(struct svc_program *prog, unsigned int bufsize, - void (*shutdown)(struct svc_serv *serv)) + sa_family_t family, void (*shutdown)(struct svc_serv *serv)) { - return __svc_create(prog, bufsize, /*npools*/1, shutdown); + return __svc_create(prog, bufsize, /*npools*/1, family, shutdown); } EXPORT_SYMBOL(svc_create); struct svc_serv * svc_create_pooled(struct svc_program *prog, unsigned int bufsize, - void (*shutdown)(struct svc_serv *serv), + sa_family_t family, void (*shutdown)(struct svc_serv *serv), svc_thread_fn func, struct module *mod) { struct svc_serv *serv; unsigned int npools = svc_pool_map_get(); - serv = __svc_create(prog, bufsize, npools, shutdown); + serv = __svc_create(prog, bufsize, npools, family, shutdown); if (serv != NULL) { serv->sv_function = func; @@ -486,8 +488,7 @@ svc_destroy(struct svc_serv *serv) if (svc_serv_is_pooled(serv)) svc_pool_map_put(); - /* Unregister service with the portmapper */ - svc_register(serv, 0, 0); + svc_unregister(serv); kfree(serv->sv_pools); kfree(serv); } @@ -718,55 +719,245 @@ svc_exit_thread(struct svc_rqst *rqstp) } EXPORT_SYMBOL(svc_exit_thread); +#ifdef CONFIG_SUNRPC_REGISTER_V4 + /* - * Register an RPC service with the local portmapper. - * To unregister a service, call this routine with - * proto and port == 0. + * Register an "inet" protocol family netid with the local + * rpcbind daemon via an rpcbind v4 SET request. + * + * No netconfig infrastructure is available in the kernel, so + * we map IP_ protocol numbers to netids by hand. + * + * Returns zero on success; a negative errno value is returned + * if any error occurs. */ -int -svc_register(struct svc_serv *serv, int proto, unsigned short port) +static int __svc_rpcb_register4(const u32 program, const u32 version, + const unsigned short protocol, + const unsigned short port) +{ + struct sockaddr_in sin = { + .sin_family = AF_INET, + .sin_addr.s_addr = htonl(INADDR_ANY), + .sin_port = htons(port), + }; + char *netid; + + switch (protocol) { + case IPPROTO_UDP: + netid = RPCBIND_NETID_UDP; + break; + case IPPROTO_TCP: + netid = RPCBIND_NETID_TCP; + break; + default: + return -EPROTONOSUPPORT; + } + + return rpcb_v4_register(program, version, + (struct sockaddr *)&sin, netid); +} + +/* + * Register an "inet6" protocol family netid with the local + * rpcbind daemon via an rpcbind v4 SET request. + * + * No netconfig infrastructure is available in the kernel, so + * we map IP_ protocol numbers to netids by hand. + * + * Returns zero on success; a negative errno value is returned + * if any error occurs. + */ +static int __svc_rpcb_register6(const u32 program, const u32 version, + const unsigned short protocol, + const unsigned short port) +{ + struct sockaddr_in6 sin6 = { + .sin6_family = AF_INET6, + .sin6_addr = IN6ADDR_ANY_INIT, + .sin6_port = htons(port), + }; + char *netid; + + switch (protocol) { + case IPPROTO_UDP: + netid = RPCBIND_NETID_UDP6; + break; + case IPPROTO_TCP: + netid = RPCBIND_NETID_TCP6; + break; + default: + return -EPROTONOSUPPORT; + } + + return rpcb_v4_register(program, version, + (struct sockaddr *)&sin6, netid); +} + +/* + * Register a kernel RPC service via rpcbind version 4. + * + * Returns zero on success; a negative errno value is returned + * if any error occurs. + */ +static int __svc_register(const u32 program, const u32 version, + const sa_family_t family, + const unsigned short protocol, + const unsigned short port) +{ + int error; + + switch (family) { + case AF_INET: + return __svc_rpcb_register4(program, version, + protocol, port); + case AF_INET6: + error = __svc_rpcb_register6(program, version, + protocol, port); + if (error < 0) + return error; + + /* + * Work around bug in some versions of Linux rpcbind + * which don't allow registration of both inet and + * inet6 netids. + * + * Error return ignored for now. + */ + __svc_rpcb_register4(program, version, + protocol, port); + return 0; + } + + return -EAFNOSUPPORT; +} + +#else /* CONFIG_SUNRPC_REGISTER_V4 */ + +/* + * Register a kernel RPC service via rpcbind version 2. + * + * Returns zero on success; a negative errno value is returned + * if any error occurs. + */ +static int __svc_register(const u32 program, const u32 version, + sa_family_t family, + const unsigned short protocol, + const unsigned short port) +{ + if (family != AF_INET) + return -EAFNOSUPPORT; + + return rpcb_register(program, version, protocol, port); +} + +#endif /* CONFIG_SUNRPC_REGISTER_V4 */ + +/** + * svc_register - register an RPC service with the local portmapper + * @serv: svc_serv struct for the service to register + * @proto: transport protocol number to advertise + * @port: port to advertise + * + * Service is registered for any address in serv's address family + */ +int svc_register(const struct svc_serv *serv, const unsigned short proto, + const unsigned short port) { struct svc_program *progp; - unsigned long flags; unsigned int i; - int error = 0, dummy; + int error = 0; - if (!port) - clear_thread_flag(TIF_SIGPENDING); + BUG_ON(proto == 0 && port == 0); for (progp = serv->sv_program; progp; progp = progp->pg_next) { for (i = 0; i < progp->pg_nvers; i++) { if (progp->pg_vers[i] == NULL) continue; - dprintk("svc: svc_register(%s, %s, %d, %d)%s\n", + dprintk("svc: svc_register(%sv%d, %s, %u, %u)%s\n", progp->pg_name, + i, proto == IPPROTO_UDP? "udp" : "tcp", port, - i, + serv->sv_family, progp->pg_vers[i]->vs_hidden? " (but not telling portmap)" : ""); if (progp->pg_vers[i]->vs_hidden) continue; - error = rpcb_register(progp->pg_prog, i, proto, port, &dummy); + error = __svc_register(progp->pg_prog, i, + serv->sv_family, proto, port); if (error < 0) break; - if (port && !dummy) { - error = -EACCES; - break; - } } } - if (!port) { - spin_lock_irqsave(¤t->sighand->siglock, flags); - recalc_sigpending(); - spin_unlock_irqrestore(¤t->sighand->siglock, flags); + return error; +} + +#ifdef CONFIG_SUNRPC_REGISTER_V4 + +static void __svc_unregister(const u32 program, const u32 version, + const char *progname) +{ + struct sockaddr_in6 sin6 = { + .sin6_family = AF_INET6, + .sin6_addr = IN6ADDR_ANY_INIT, + .sin6_port = 0, + }; + int error; + + error = rpcb_v4_register(program, version, + (struct sockaddr *)&sin6, ""); + dprintk("svc: %s(%sv%u), error %d\n", + __func__, progname, version, error); +} + +#else /* CONFIG_SUNRPC_REGISTER_V4 */ + +static void __svc_unregister(const u32 program, const u32 version, + const char *progname) +{ + int error; + + error = rpcb_register(program, version, 0, 0); + dprintk("svc: %s(%sv%u), error %d\n", + __func__, progname, version, error); +} + +#endif /* CONFIG_SUNRPC_REGISTER_V4 */ + +/* + * All netids, bind addresses and ports registered for [program, version] + * are removed from the local rpcbind database (if the service is not + * hidden) to make way for a new instance of the service. + * + * The result of unregistration is reported via dprintk for those who want + * verification of the result, but is otherwise not important. + */ +static void svc_unregister(const struct svc_serv *serv) +{ + struct svc_program *progp; + unsigned long flags; + unsigned int i; + + clear_thread_flag(TIF_SIGPENDING); + + for (progp = serv->sv_program; progp; progp = progp->pg_next) { + for (i = 0; i < progp->pg_nvers; i++) { + if (progp->pg_vers[i] == NULL) + continue; + if (progp->pg_vers[i]->vs_hidden) + continue; + + __svc_unregister(progp->pg_prog, i, progp->pg_name); + } } - return error; + spin_lock_irqsave(¤t->sighand->siglock, flags); + recalc_sigpending(); + spin_unlock_irqrestore(¤t->sighand->siglock, flags); } /* diff --git a/net/sunrpc/svc_xprt.c b/net/sunrpc/svc_xprt.c index e46c825f4954..bf5b5cdafebf 100644 --- a/net/sunrpc/svc_xprt.c +++ b/net/sunrpc/svc_xprt.c @@ -159,15 +159,44 @@ void svc_xprt_init(struct svc_xprt_class *xcl, struct svc_xprt *xprt, } EXPORT_SYMBOL_GPL(svc_xprt_init); -int svc_create_xprt(struct svc_serv *serv, char *xprt_name, unsigned short port, - int flags) +static struct svc_xprt *__svc_xpo_create(struct svc_xprt_class *xcl, + struct svc_serv *serv, + unsigned short port, int flags) { - struct svc_xprt_class *xcl; struct sockaddr_in sin = { .sin_family = AF_INET, .sin_addr.s_addr = htonl(INADDR_ANY), .sin_port = htons(port), }; + struct sockaddr_in6 sin6 = { + .sin6_family = AF_INET6, + .sin6_addr = IN6ADDR_ANY_INIT, + .sin6_port = htons(port), + }; + struct sockaddr *sap; + size_t len; + + switch (serv->sv_family) { + case AF_INET: + sap = (struct sockaddr *)&sin; + len = sizeof(sin); + break; + case AF_INET6: + sap = (struct sockaddr *)&sin6; + len = sizeof(sin6); + break; + default: + return ERR_PTR(-EAFNOSUPPORT); + } + + return xcl->xcl_ops->xpo_create(serv, sap, len, flags); +} + +int svc_create_xprt(struct svc_serv *serv, char *xprt_name, unsigned short port, + int flags) +{ + struct svc_xprt_class *xcl; + dprintk("svc: creating transport %s[%d]\n", xprt_name, port); spin_lock(&svc_xprt_class_lock); list_for_each_entry(xcl, &svc_xprt_class_list, xcl_list) { @@ -180,9 +209,7 @@ int svc_create_xprt(struct svc_serv *serv, char *xprt_name, unsigned short port, goto err; spin_unlock(&svc_xprt_class_lock); - newxprt = xcl->xcl_ops-> - xpo_create(serv, (struct sockaddr *)&sin, sizeof(sin), - flags); + newxprt = __svc_xpo_create(xcl, serv, port, flags); if (IS_ERR(newxprt)) { module_put(xcl->xcl_owner); return PTR_ERR(newxprt); diff --git a/net/sunrpc/svcsock.c b/net/sunrpc/svcsock.c index 3e65719f1ef6..95293f549e9c 100644 --- a/net/sunrpc/svcsock.c +++ b/net/sunrpc/svcsock.c @@ -1114,6 +1114,7 @@ static struct svc_sock *svc_setup_socket(struct svc_serv *serv, struct svc_sock *svsk; struct sock *inet; int pmap_register = !(flags & SVC_SOCK_ANONYMOUS); + int val; dprintk("svc: svc_setup_socket %p\n", sock); if (!(svsk = kzalloc(sizeof(*svsk), GFP_KERNEL))) { @@ -1146,6 +1147,18 @@ static struct svc_sock *svc_setup_socket(struct svc_serv *serv, else svc_tcp_init(svsk, serv); + /* + * We start one listener per sv_serv. We want AF_INET + * requests to be automatically shunted to our AF_INET6 + * listener using a mapped IPv4 address. Make sure + * no-one starts an equivalent IPv4 listener, which + * would steal our incoming connections. + */ + val = 0; + if (serv->sv_family == AF_INET6) + kernel_setsockopt(sock, SOL_IPV6, IPV6_V6ONLY, + (char *)&val, sizeof(val)); + dprintk("svc: svc_setup_socket created %p (inet %p)\n", svsk, svsk->sk_sk); @@ -1154,8 +1167,7 @@ static struct svc_sock *svc_setup_socket(struct svc_serv *serv, int svc_addsock(struct svc_serv *serv, int fd, - char *name_return, - int *proto) + char *name_return) { int err = 0; struct socket *so = sockfd_lookup(fd, &err); @@ -1190,7 +1202,6 @@ int svc_addsock(struct svc_serv *serv, sockfd_put(so); return err; } - if (proto) *proto = so->sk->sk_protocol; return one_sock_name(name_return, svsk); } EXPORT_SYMBOL_GPL(svc_addsock); diff --git a/net/sunrpc/xprtrdma/svc_rdma_recvfrom.c b/net/sunrpc/xprtrdma/svc_rdma_recvfrom.c index 74de31a06616..a4756576d687 100644 --- a/net/sunrpc/xprtrdma/svc_rdma_recvfrom.c +++ b/net/sunrpc/xprtrdma/svc_rdma_recvfrom.c @@ -116,7 +116,7 @@ static void rdma_build_arg_xdr(struct svc_rqst *rqstp, * * Assumptions: * - chunk[0]->position points to pages[0] at an offset of 0 - * - pages[] is not physically or virtually contigous and consists of + * - pages[] is not physically or virtually contiguous and consists of * PAGE_SIZE elements. * * Output: @@ -125,7 +125,7 @@ static void rdma_build_arg_xdr(struct svc_rqst *rqstp, * chunk in the read list * */ -static int rdma_rcl_to_sge(struct svcxprt_rdma *xprt, +static int map_read_chunks(struct svcxprt_rdma *xprt, struct svc_rqst *rqstp, struct svc_rdma_op_ctxt *head, struct rpcrdma_msg *rmsgp, @@ -211,26 +211,128 @@ static int rdma_rcl_to_sge(struct svcxprt_rdma *xprt, return sge_no; } -static void rdma_set_ctxt_sge(struct svcxprt_rdma *xprt, - struct svc_rdma_op_ctxt *ctxt, - struct kvec *vec, - u64 *sgl_offset, - int count) +/* Map a read-chunk-list to an XDR and fast register the page-list. + * + * Assumptions: + * - chunk[0] position points to pages[0] at an offset of 0 + * - pages[] will be made physically contiguous by creating a one-off memory + * region using the fastreg verb. + * - byte_count is # of bytes in read-chunk-list + * - ch_count is # of chunks in read-chunk-list + * + * Output: + * - sge array pointing into pages[] array. + * - chunk_sge array specifying sge index and count for each + * chunk in the read list + */ +static int fast_reg_read_chunks(struct svcxprt_rdma *xprt, + struct svc_rqst *rqstp, + struct svc_rdma_op_ctxt *head, + struct rpcrdma_msg *rmsgp, + struct svc_rdma_req_map *rpl_map, + struct svc_rdma_req_map *chl_map, + int ch_count, + int byte_count) +{ + int page_no; + int ch_no; + u32 offset; + struct rpcrdma_read_chunk *ch; + struct svc_rdma_fastreg_mr *frmr; + int ret = 0; + + frmr = svc_rdma_get_frmr(xprt); + if (IS_ERR(frmr)) + return -ENOMEM; + + head->frmr = frmr; + head->arg.head[0] = rqstp->rq_arg.head[0]; + head->arg.tail[0] = rqstp->rq_arg.tail[0]; + head->arg.pages = &head->pages[head->count]; + head->hdr_count = head->count; /* save count of hdr pages */ + head->arg.page_base = 0; + head->arg.page_len = byte_count; + head->arg.len = rqstp->rq_arg.len + byte_count; + head->arg.buflen = rqstp->rq_arg.buflen + byte_count; + + /* Fast register the page list */ + frmr->kva = page_address(rqstp->rq_arg.pages[0]); + frmr->direction = DMA_FROM_DEVICE; + frmr->access_flags = (IB_ACCESS_LOCAL_WRITE|IB_ACCESS_REMOTE_WRITE); + frmr->map_len = byte_count; + frmr->page_list_len = PAGE_ALIGN(byte_count) >> PAGE_SHIFT; + for (page_no = 0; page_no < frmr->page_list_len; page_no++) { + frmr->page_list->page_list[page_no] = + ib_dma_map_single(xprt->sc_cm_id->device, + page_address(rqstp->rq_arg.pages[page_no]), + PAGE_SIZE, DMA_TO_DEVICE); + if (ib_dma_mapping_error(xprt->sc_cm_id->device, + frmr->page_list->page_list[page_no])) + goto fatal_err; + atomic_inc(&xprt->sc_dma_used); + head->arg.pages[page_no] = rqstp->rq_arg.pages[page_no]; + } + head->count += page_no; + + /* rq_respages points one past arg pages */ + rqstp->rq_respages = &rqstp->rq_arg.pages[page_no]; + + /* Create the reply and chunk maps */ + offset = 0; + ch = (struct rpcrdma_read_chunk *)&rmsgp->rm_body.rm_chunks[0]; + for (ch_no = 0; ch_no < ch_count; ch_no++) { + rpl_map->sge[ch_no].iov_base = frmr->kva + offset; + rpl_map->sge[ch_no].iov_len = ch->rc_target.rs_length; + chl_map->ch[ch_no].count = 1; + chl_map->ch[ch_no].start = ch_no; + offset += ch->rc_target.rs_length; + ch++; + } + + ret = svc_rdma_fastreg(xprt, frmr); + if (ret) + goto fatal_err; + + return ch_no; + + fatal_err: + printk("svcrdma: error fast registering xdr for xprt %p", xprt); + svc_rdma_put_frmr(xprt, frmr); + return -EIO; +} + +static int rdma_set_ctxt_sge(struct svcxprt_rdma *xprt, + struct svc_rdma_op_ctxt *ctxt, + struct svc_rdma_fastreg_mr *frmr, + struct kvec *vec, + u64 *sgl_offset, + int count) { int i; ctxt->count = count; ctxt->direction = DMA_FROM_DEVICE; for (i = 0; i < count; i++) { - atomic_inc(&xprt->sc_dma_used); - ctxt->sge[i].addr = - ib_dma_map_single(xprt->sc_cm_id->device, - vec[i].iov_base, vec[i].iov_len, - DMA_FROM_DEVICE); + ctxt->sge[i].length = 0; /* in case map fails */ + if (!frmr) { + ctxt->sge[i].addr = + ib_dma_map_single(xprt->sc_cm_id->device, + vec[i].iov_base, + vec[i].iov_len, + DMA_FROM_DEVICE); + if (ib_dma_mapping_error(xprt->sc_cm_id->device, + ctxt->sge[i].addr)) + return -EINVAL; + ctxt->sge[i].lkey = xprt->sc_dma_lkey; + atomic_inc(&xprt->sc_dma_used); + } else { + ctxt->sge[i].addr = (unsigned long)vec[i].iov_base; + ctxt->sge[i].lkey = frmr->mr->lkey; + } ctxt->sge[i].length = vec[i].iov_len; - ctxt->sge[i].lkey = xprt->sc_phys_mr->lkey; *sgl_offset = *sgl_offset + vec[i].iov_len; } + return 0; } static int rdma_read_max_sge(struct svcxprt_rdma *xprt, int sge_count) @@ -278,6 +380,7 @@ static int rdma_read_xdr(struct svcxprt_rdma *xprt, struct svc_rdma_op_ctxt *hdr_ctxt) { struct ib_send_wr read_wr; + struct ib_send_wr inv_wr; int err = 0; int ch_no; int ch_count; @@ -301,9 +404,20 @@ static int rdma_read_xdr(struct svcxprt_rdma *xprt, svc_rdma_rcl_chunk_counts(ch, &ch_count, &byte_count); if (ch_count > RPCSVC_MAXPAGES) return -EINVAL; - sge_count = rdma_rcl_to_sge(xprt, rqstp, hdr_ctxt, rmsgp, - rpl_map, chl_map, - ch_count, byte_count); + + if (!xprt->sc_frmr_pg_list_len) + sge_count = map_read_chunks(xprt, rqstp, hdr_ctxt, rmsgp, + rpl_map, chl_map, ch_count, + byte_count); + else + sge_count = fast_reg_read_chunks(xprt, rqstp, hdr_ctxt, rmsgp, + rpl_map, chl_map, ch_count, + byte_count); + if (sge_count < 0) { + err = -EIO; + goto out; + } + sgl_offset = 0; ch_no = 0; @@ -312,13 +426,16 @@ static int rdma_read_xdr(struct svcxprt_rdma *xprt, next_sge: ctxt = svc_rdma_get_context(xprt); ctxt->direction = DMA_FROM_DEVICE; + ctxt->frmr = hdr_ctxt->frmr; + ctxt->read_hdr = NULL; clear_bit(RDMACTXT_F_LAST_CTXT, &ctxt->flags); + clear_bit(RDMACTXT_F_FAST_UNREG, &ctxt->flags); /* Prepare READ WR */ memset(&read_wr, 0, sizeof read_wr); - ctxt->wr_op = IB_WR_RDMA_READ; read_wr.wr_id = (unsigned long)ctxt; read_wr.opcode = IB_WR_RDMA_READ; + ctxt->wr_op = read_wr.opcode; read_wr.send_flags = IB_SEND_SIGNALED; read_wr.wr.rdma.rkey = ch->rc_target.rs_handle; read_wr.wr.rdma.remote_addr = @@ -327,10 +444,15 @@ next_sge: read_wr.sg_list = ctxt->sge; read_wr.num_sge = rdma_read_max_sge(xprt, chl_map->ch[ch_no].count); - rdma_set_ctxt_sge(xprt, ctxt, - &rpl_map->sge[chl_map->ch[ch_no].start], - &sgl_offset, - read_wr.num_sge); + err = rdma_set_ctxt_sge(xprt, ctxt, hdr_ctxt->frmr, + &rpl_map->sge[chl_map->ch[ch_no].start], + &sgl_offset, + read_wr.num_sge); + if (err) { + svc_rdma_unmap_dma(ctxt); + svc_rdma_put_context(ctxt, 0); + goto out; + } if (((ch+1)->rc_discrim == 0) && (read_wr.num_sge == chl_map->ch[ch_no].count)) { /* @@ -339,6 +461,29 @@ next_sge: * the client and the RPC needs to be enqueued. */ set_bit(RDMACTXT_F_LAST_CTXT, &ctxt->flags); + if (hdr_ctxt->frmr) { + set_bit(RDMACTXT_F_FAST_UNREG, &ctxt->flags); + /* + * Invalidate the local MR used to map the data + * sink. + */ + if (xprt->sc_dev_caps & + SVCRDMA_DEVCAP_READ_W_INV) { + read_wr.opcode = + IB_WR_RDMA_READ_WITH_INV; + ctxt->wr_op = read_wr.opcode; + read_wr.ex.invalidate_rkey = + ctxt->frmr->mr->lkey; + } else { + /* Prepare INVALIDATE WR */ + memset(&inv_wr, 0, sizeof inv_wr); + inv_wr.opcode = IB_WR_LOCAL_INV; + inv_wr.send_flags = IB_SEND_SIGNALED; + inv_wr.ex.invalidate_rkey = + hdr_ctxt->frmr->mr->lkey; + read_wr.next = &inv_wr; + } + } ctxt->read_hdr = hdr_ctxt; } /* Post the read */ diff --git a/net/sunrpc/xprtrdma/svc_rdma_sendto.c b/net/sunrpc/xprtrdma/svc_rdma_sendto.c index 84d328329d98..9a7a8e7ae038 100644 --- a/net/sunrpc/xprtrdma/svc_rdma_sendto.c +++ b/net/sunrpc/xprtrdma/svc_rdma_sendto.c @@ -69,9 +69,127 @@ * array is only concerned with the reply we are assured that we have * on extra page for the RPCRMDA header. */ -static void xdr_to_sge(struct svcxprt_rdma *xprt, - struct xdr_buf *xdr, - struct svc_rdma_req_map *vec) +int fast_reg_xdr(struct svcxprt_rdma *xprt, + struct xdr_buf *xdr, + struct svc_rdma_req_map *vec) +{ + int sge_no; + u32 sge_bytes; + u32 page_bytes; + u32 page_off; + int page_no = 0; + u8 *frva; + struct svc_rdma_fastreg_mr *frmr; + + frmr = svc_rdma_get_frmr(xprt); + if (IS_ERR(frmr)) + return -ENOMEM; + vec->frmr = frmr; + + /* Skip the RPCRDMA header */ + sge_no = 1; + + /* Map the head. */ + frva = (void *)((unsigned long)(xdr->head[0].iov_base) & PAGE_MASK); + vec->sge[sge_no].iov_base = xdr->head[0].iov_base; + vec->sge[sge_no].iov_len = xdr->head[0].iov_len; + vec->count = 2; + sge_no++; + + /* Build the FRMR */ + frmr->kva = frva; + frmr->direction = DMA_TO_DEVICE; + frmr->access_flags = 0; + frmr->map_len = PAGE_SIZE; + frmr->page_list_len = 1; + frmr->page_list->page_list[page_no] = + ib_dma_map_single(xprt->sc_cm_id->device, + (void *)xdr->head[0].iov_base, + PAGE_SIZE, DMA_TO_DEVICE); + if (ib_dma_mapping_error(xprt->sc_cm_id->device, + frmr->page_list->page_list[page_no])) + goto fatal_err; + atomic_inc(&xprt->sc_dma_used); + + page_off = xdr->page_base; + page_bytes = xdr->page_len + page_off; + if (!page_bytes) + goto encode_tail; + + /* Map the pages */ + vec->sge[sge_no].iov_base = frva + frmr->map_len + page_off; + vec->sge[sge_no].iov_len = page_bytes; + sge_no++; + while (page_bytes) { + struct page *page; + + page = xdr->pages[page_no++]; + sge_bytes = min_t(u32, page_bytes, (PAGE_SIZE - page_off)); + page_bytes -= sge_bytes; + + frmr->page_list->page_list[page_no] = + ib_dma_map_page(xprt->sc_cm_id->device, page, 0, + PAGE_SIZE, DMA_TO_DEVICE); + if (ib_dma_mapping_error(xprt->sc_cm_id->device, + frmr->page_list->page_list[page_no])) + goto fatal_err; + + atomic_inc(&xprt->sc_dma_used); + page_off = 0; /* reset for next time through loop */ + frmr->map_len += PAGE_SIZE; + frmr->page_list_len++; + } + vec->count++; + + encode_tail: + /* Map tail */ + if (0 == xdr->tail[0].iov_len) + goto done; + + vec->count++; + vec->sge[sge_no].iov_len = xdr->tail[0].iov_len; + + if (((unsigned long)xdr->tail[0].iov_base & PAGE_MASK) == + ((unsigned long)xdr->head[0].iov_base & PAGE_MASK)) { + /* + * If head and tail use the same page, we don't need + * to map it again. + */ + vec->sge[sge_no].iov_base = xdr->tail[0].iov_base; + } else { + void *va; + + /* Map another page for the tail */ + page_off = (unsigned long)xdr->tail[0].iov_base & ~PAGE_MASK; + va = (void *)((unsigned long)xdr->tail[0].iov_base & PAGE_MASK); + vec->sge[sge_no].iov_base = frva + frmr->map_len + page_off; + + frmr->page_list->page_list[page_no] = + ib_dma_map_single(xprt->sc_cm_id->device, va, PAGE_SIZE, + DMA_TO_DEVICE); + if (ib_dma_mapping_error(xprt->sc_cm_id->device, + frmr->page_list->page_list[page_no])) + goto fatal_err; + atomic_inc(&xprt->sc_dma_used); + frmr->map_len += PAGE_SIZE; + frmr->page_list_len++; + } + + done: + if (svc_rdma_fastreg(xprt, frmr)) + goto fatal_err; + + return 0; + + fatal_err: + printk("svcrdma: Error fast registering memory for xprt %p\n", xprt); + svc_rdma_put_frmr(xprt, frmr); + return -EIO; +} + +static int map_xdr(struct svcxprt_rdma *xprt, + struct xdr_buf *xdr, + struct svc_rdma_req_map *vec) { int sge_max = (xdr->len+PAGE_SIZE-1) / PAGE_SIZE + 3; int sge_no; @@ -83,6 +201,9 @@ static void xdr_to_sge(struct svcxprt_rdma *xprt, BUG_ON(xdr->len != (xdr->head[0].iov_len + xdr->page_len + xdr->tail[0].iov_len)); + if (xprt->sc_frmr_pg_list_len) + return fast_reg_xdr(xprt, xdr, vec); + /* Skip the first sge, this is for the RPCRDMA header */ sge_no = 1; @@ -116,9 +237,12 @@ static void xdr_to_sge(struct svcxprt_rdma *xprt, BUG_ON(sge_no > sge_max); vec->count = sge_no; + return 0; } /* Assumptions: + * - We are using FRMR + * - or - * - The specified write_len can be represented in sc_max_sge * PAGE_SIZE */ static int send_write(struct svcxprt_rdma *xprt, struct svc_rqst *rqstp, @@ -158,30 +282,35 @@ static int send_write(struct svcxprt_rdma *xprt, struct svc_rqst *rqstp, sge_no = 0; /* Copy the remaining SGE */ - while (bc != 0 && xdr_sge_no < vec->count) { - sge[sge_no].lkey = xprt->sc_phys_mr->lkey; - sge_bytes = min((size_t)bc, - (size_t)(vec->sge[xdr_sge_no].iov_len-sge_off)); + while (bc != 0) { + sge_bytes = min_t(size_t, + bc, vec->sge[xdr_sge_no].iov_len-sge_off); sge[sge_no].length = sge_bytes; - atomic_inc(&xprt->sc_dma_used); - sge[sge_no].addr = - ib_dma_map_single(xprt->sc_cm_id->device, - (void *) - vec->sge[xdr_sge_no].iov_base + sge_off, - sge_bytes, DMA_TO_DEVICE); - if (dma_mapping_error(xprt->sc_cm_id->device->dma_device, - sge[sge_no].addr)) - goto err; + if (!vec->frmr) { + sge[sge_no].addr = + ib_dma_map_single(xprt->sc_cm_id->device, + (void *) + vec->sge[xdr_sge_no].iov_base + sge_off, + sge_bytes, DMA_TO_DEVICE); + if (ib_dma_mapping_error(xprt->sc_cm_id->device, + sge[sge_no].addr)) + goto err; + atomic_inc(&xprt->sc_dma_used); + sge[sge_no].lkey = xprt->sc_dma_lkey; + } else { + sge[sge_no].addr = (unsigned long) + vec->sge[xdr_sge_no].iov_base + sge_off; + sge[sge_no].lkey = vec->frmr->mr->lkey; + } + ctxt->count++; + ctxt->frmr = vec->frmr; sge_off = 0; sge_no++; - ctxt->count++; xdr_sge_no++; + BUG_ON(xdr_sge_no > vec->count); bc -= sge_bytes; } - BUG_ON(bc != 0); - BUG_ON(xdr_sge_no > vec->count); - /* Prepare WRITE WR */ memset(&write_wr, 0, sizeof write_wr); ctxt->wr_op = IB_WR_RDMA_WRITE; @@ -226,7 +355,10 @@ static int send_write_chunks(struct svcxprt_rdma *xprt, res_ary = (struct rpcrdma_write_array *) &rdma_resp->rm_body.rm_chunks[1]; - max_write = xprt->sc_max_sge * PAGE_SIZE; + if (vec->frmr) + max_write = vec->frmr->map_len; + else + max_write = xprt->sc_max_sge * PAGE_SIZE; /* Write chunks start at the pagelist */ for (xdr_off = rqstp->rq_res.head[0].iov_len, chunk_no = 0; @@ -297,7 +429,10 @@ static int send_reply_chunks(struct svcxprt_rdma *xprt, res_ary = (struct rpcrdma_write_array *) &rdma_resp->rm_body.rm_chunks[2]; - max_write = xprt->sc_max_sge * PAGE_SIZE; + if (vec->frmr) + max_write = vec->frmr->map_len; + else + max_write = xprt->sc_max_sge * PAGE_SIZE; /* xdr offset starts at RPC message */ for (xdr_off = 0, chunk_no = 0; @@ -307,7 +442,6 @@ static int send_reply_chunks(struct svcxprt_rdma *xprt, ch = &arg_ary->wc_array[chunk_no].wc_target; write_len = min(xfer_len, ch->rs_length); - /* Prepare the reply chunk given the length actually * written */ rs_offset = get_unaligned(&(ch->rs_offset)); @@ -366,6 +500,7 @@ static int send_reply(struct svcxprt_rdma *rdma, int byte_count) { struct ib_send_wr send_wr; + struct ib_send_wr inv_wr; int sge_no; int sge_bytes; int page_no; @@ -385,27 +520,45 @@ static int send_reply(struct svcxprt_rdma *rdma, /* Prepare the context */ ctxt->pages[0] = page; ctxt->count = 1; + ctxt->frmr = vec->frmr; + if (vec->frmr) + set_bit(RDMACTXT_F_FAST_UNREG, &ctxt->flags); + else + clear_bit(RDMACTXT_F_FAST_UNREG, &ctxt->flags); /* Prepare the SGE for the RPCRDMA Header */ - atomic_inc(&rdma->sc_dma_used); ctxt->sge[0].addr = ib_dma_map_page(rdma->sc_cm_id->device, page, 0, PAGE_SIZE, DMA_TO_DEVICE); + if (ib_dma_mapping_error(rdma->sc_cm_id->device, ctxt->sge[0].addr)) + goto err; + atomic_inc(&rdma->sc_dma_used); + ctxt->direction = DMA_TO_DEVICE; + ctxt->sge[0].length = svc_rdma_xdr_get_reply_hdr_len(rdma_resp); - ctxt->sge[0].lkey = rdma->sc_phys_mr->lkey; + ctxt->sge[0].lkey = rdma->sc_dma_lkey; /* Determine how many of our SGE are to be transmitted */ for (sge_no = 1; byte_count && sge_no < vec->count; sge_no++) { sge_bytes = min_t(size_t, vec->sge[sge_no].iov_len, byte_count); byte_count -= sge_bytes; - atomic_inc(&rdma->sc_dma_used); - ctxt->sge[sge_no].addr = - ib_dma_map_single(rdma->sc_cm_id->device, - vec->sge[sge_no].iov_base, - sge_bytes, DMA_TO_DEVICE); + if (!vec->frmr) { + ctxt->sge[sge_no].addr = + ib_dma_map_single(rdma->sc_cm_id->device, + vec->sge[sge_no].iov_base, + sge_bytes, DMA_TO_DEVICE); + if (ib_dma_mapping_error(rdma->sc_cm_id->device, + ctxt->sge[sge_no].addr)) + goto err; + atomic_inc(&rdma->sc_dma_used); + ctxt->sge[sge_no].lkey = rdma->sc_dma_lkey; + } else { + ctxt->sge[sge_no].addr = (unsigned long) + vec->sge[sge_no].iov_base; + ctxt->sge[sge_no].lkey = vec->frmr->mr->lkey; + } ctxt->sge[sge_no].length = sge_bytes; - ctxt->sge[sge_no].lkey = rdma->sc_phys_mr->lkey; } BUG_ON(byte_count != 0); @@ -417,11 +570,16 @@ static int send_reply(struct svcxprt_rdma *rdma, ctxt->pages[page_no+1] = rqstp->rq_respages[page_no]; ctxt->count++; rqstp->rq_respages[page_no] = NULL; - /* If there are more pages than SGE, terminate SGE list */ + /* + * If there are more pages than SGE, terminate SGE + * list so that svc_rdma_unmap_dma doesn't attempt to + * unmap garbage. + */ if (page_no+1 >= sge_no) ctxt->sge[page_no+1].length = 0; } BUG_ON(sge_no > rdma->sc_max_sge); + BUG_ON(sge_no > ctxt->count); memset(&send_wr, 0, sizeof send_wr); ctxt->wr_op = IB_WR_SEND; send_wr.wr_id = (unsigned long)ctxt; @@ -429,12 +587,26 @@ static int send_reply(struct svcxprt_rdma *rdma, send_wr.num_sge = sge_no; send_wr.opcode = IB_WR_SEND; send_wr.send_flags = IB_SEND_SIGNALED; + if (vec->frmr) { + /* Prepare INVALIDATE WR */ + memset(&inv_wr, 0, sizeof inv_wr); + inv_wr.opcode = IB_WR_LOCAL_INV; + inv_wr.send_flags = IB_SEND_SIGNALED; + inv_wr.ex.invalidate_rkey = + vec->frmr->mr->lkey; + send_wr.next = &inv_wr; + } ret = svc_rdma_send(rdma, &send_wr); if (ret) - svc_rdma_put_context(ctxt, 1); + goto err; - return ret; + return 0; + + err: + svc_rdma_put_frmr(rdma, vec->frmr); + svc_rdma_put_context(ctxt, 1); + return -EIO; } void svc_rdma_prep_reply_hdr(struct svc_rqst *rqstp) @@ -477,8 +649,9 @@ int svc_rdma_sendto(struct svc_rqst *rqstp) ctxt = svc_rdma_get_context(rdma); ctxt->direction = DMA_TO_DEVICE; vec = svc_rdma_get_req_map(); - xdr_to_sge(rdma, &rqstp->rq_res, vec); - + ret = map_xdr(rdma, &rqstp->rq_res, vec); + if (ret) + goto err0; inline_bytes = rqstp->rq_res.len; /* Create the RDMA response header */ @@ -498,7 +671,7 @@ int svc_rdma_sendto(struct svc_rqst *rqstp) if (ret < 0) { printk(KERN_ERR "svcrdma: failed to send write chunks, rc=%d\n", ret); - goto error; + goto err1; } inline_bytes -= ret; @@ -508,7 +681,7 @@ int svc_rdma_sendto(struct svc_rqst *rqstp) if (ret < 0) { printk(KERN_ERR "svcrdma: failed to send reply chunks, rc=%d\n", ret); - goto error; + goto err1; } inline_bytes -= ret; @@ -517,9 +690,11 @@ int svc_rdma_sendto(struct svc_rqst *rqstp) svc_rdma_put_req_map(vec); dprintk("svcrdma: send_reply returns %d\n", ret); return ret; - error: + + err1: + put_page(res_page); + err0: svc_rdma_put_req_map(vec); svc_rdma_put_context(ctxt, 0); - put_page(res_page); return ret; } diff --git a/net/sunrpc/xprtrdma/svc_rdma_transport.c b/net/sunrpc/xprtrdma/svc_rdma_transport.c index 900cb69728c6..6fb493cbd29f 100644 --- a/net/sunrpc/xprtrdma/svc_rdma_transport.c +++ b/net/sunrpc/xprtrdma/svc_rdma_transport.c @@ -100,20 +100,29 @@ struct svc_rdma_op_ctxt *svc_rdma_get_context(struct svcxprt_rdma *xprt) ctxt->xprt = xprt; INIT_LIST_HEAD(&ctxt->dto_q); ctxt->count = 0; + ctxt->frmr = NULL; atomic_inc(&xprt->sc_ctxt_used); return ctxt; } -static void svc_rdma_unmap_dma(struct svc_rdma_op_ctxt *ctxt) +void svc_rdma_unmap_dma(struct svc_rdma_op_ctxt *ctxt) { struct svcxprt_rdma *xprt = ctxt->xprt; int i; for (i = 0; i < ctxt->count && ctxt->sge[i].length; i++) { - atomic_dec(&xprt->sc_dma_used); - ib_dma_unmap_single(xprt->sc_cm_id->device, - ctxt->sge[i].addr, - ctxt->sge[i].length, - ctxt->direction); + /* + * Unmap the DMA addr in the SGE if the lkey matches + * the sc_dma_lkey, otherwise, ignore it since it is + * an FRMR lkey and will be unmapped later when the + * last WR that uses it completes. + */ + if (ctxt->sge[i].lkey == xprt->sc_dma_lkey) { + atomic_dec(&xprt->sc_dma_used); + ib_dma_unmap_single(xprt->sc_cm_id->device, + ctxt->sge[i].addr, + ctxt->sge[i].length, + ctxt->direction); + } } } @@ -150,6 +159,7 @@ struct svc_rdma_req_map *svc_rdma_get_req_map(void) schedule_timeout_uninterruptible(msecs_to_jiffies(500)); } map->count = 0; + map->frmr = NULL; return map; } @@ -316,6 +326,50 @@ static void rq_cq_reap(struct svcxprt_rdma *xprt) } /* + * Processs a completion context + */ +static void process_context(struct svcxprt_rdma *xprt, + struct svc_rdma_op_ctxt *ctxt) +{ + svc_rdma_unmap_dma(ctxt); + + switch (ctxt->wr_op) { + case IB_WR_SEND: + if (test_bit(RDMACTXT_F_FAST_UNREG, &ctxt->flags)) + svc_rdma_put_frmr(xprt, ctxt->frmr); + svc_rdma_put_context(ctxt, 1); + break; + + case IB_WR_RDMA_WRITE: + svc_rdma_put_context(ctxt, 0); + break; + + case IB_WR_RDMA_READ: + case IB_WR_RDMA_READ_WITH_INV: + if (test_bit(RDMACTXT_F_LAST_CTXT, &ctxt->flags)) { + struct svc_rdma_op_ctxt *read_hdr = ctxt->read_hdr; + BUG_ON(!read_hdr); + if (test_bit(RDMACTXT_F_FAST_UNREG, &ctxt->flags)) + svc_rdma_put_frmr(xprt, ctxt->frmr); + spin_lock_bh(&xprt->sc_rq_dto_lock); + set_bit(XPT_DATA, &xprt->sc_xprt.xpt_flags); + list_add_tail(&read_hdr->dto_q, + &xprt->sc_read_complete_q); + spin_unlock_bh(&xprt->sc_rq_dto_lock); + svc_xprt_enqueue(&xprt->sc_xprt); + } + svc_rdma_put_context(ctxt, 0); + break; + + default: + printk(KERN_ERR "svcrdma: unexpected completion type, " + "opcode=%d\n", + ctxt->wr_op); + break; + } +} + +/* * Send Queue Completion Handler - potentially called on interrupt context. * * Note that caller must hold a transport reference. @@ -327,17 +381,12 @@ static void sq_cq_reap(struct svcxprt_rdma *xprt) struct ib_cq *cq = xprt->sc_sq_cq; int ret; - if (!test_and_clear_bit(RDMAXPRT_SQ_PENDING, &xprt->sc_flags)) return; ib_req_notify_cq(xprt->sc_sq_cq, IB_CQ_NEXT_COMP); atomic_inc(&rdma_stat_sq_poll); while ((ret = ib_poll_cq(cq, 1, &wc)) > 0) { - ctxt = (struct svc_rdma_op_ctxt *)(unsigned long)wc.wr_id; - xprt = ctxt->xprt; - - svc_rdma_unmap_dma(ctxt); if (wc.status != IB_WC_SUCCESS) /* Close the transport */ set_bit(XPT_CLOSE, &xprt->sc_xprt.xpt_flags); @@ -346,35 +395,10 @@ static void sq_cq_reap(struct svcxprt_rdma *xprt) atomic_dec(&xprt->sc_sq_count); wake_up(&xprt->sc_send_wait); - switch (ctxt->wr_op) { - case IB_WR_SEND: - svc_rdma_put_context(ctxt, 1); - break; - - case IB_WR_RDMA_WRITE: - svc_rdma_put_context(ctxt, 0); - break; - - case IB_WR_RDMA_READ: - if (test_bit(RDMACTXT_F_LAST_CTXT, &ctxt->flags)) { - struct svc_rdma_op_ctxt *read_hdr = ctxt->read_hdr; - BUG_ON(!read_hdr); - spin_lock_bh(&xprt->sc_rq_dto_lock); - set_bit(XPT_DATA, &xprt->sc_xprt.xpt_flags); - list_add_tail(&read_hdr->dto_q, - &xprt->sc_read_complete_q); - spin_unlock_bh(&xprt->sc_rq_dto_lock); - svc_xprt_enqueue(&xprt->sc_xprt); - } - svc_rdma_put_context(ctxt, 0); - break; + ctxt = (struct svc_rdma_op_ctxt *)(unsigned long)wc.wr_id; + if (ctxt) + process_context(xprt, ctxt); - default: - printk(KERN_ERR "svcrdma: unexpected completion type, " - "opcode=%d, status=%d\n", - wc.opcode, wc.status); - break; - } svc_xprt_put(&xprt->sc_xprt); } @@ -425,10 +449,12 @@ static struct svcxprt_rdma *rdma_create_xprt(struct svc_serv *serv, INIT_LIST_HEAD(&cma_xprt->sc_dto_q); INIT_LIST_HEAD(&cma_xprt->sc_rq_dto_q); INIT_LIST_HEAD(&cma_xprt->sc_read_complete_q); + INIT_LIST_HEAD(&cma_xprt->sc_frmr_q); init_waitqueue_head(&cma_xprt->sc_send_wait); spin_lock_init(&cma_xprt->sc_lock); spin_lock_init(&cma_xprt->sc_rq_dto_lock); + spin_lock_init(&cma_xprt->sc_frmr_q_lock); cma_xprt->sc_ord = svcrdma_ord; @@ -462,7 +488,7 @@ int svc_rdma_post_recv(struct svcxprt_rdma *xprt) struct ib_recv_wr recv_wr, *bad_recv_wr; struct svc_rdma_op_ctxt *ctxt; struct page *page; - unsigned long pa; + dma_addr_t pa; int sge_no; int buflen; int ret; @@ -474,13 +500,15 @@ int svc_rdma_post_recv(struct svcxprt_rdma *xprt) BUG_ON(sge_no >= xprt->sc_max_sge); page = svc_rdma_get_page(); ctxt->pages[sge_no] = page; - atomic_inc(&xprt->sc_dma_used); pa = ib_dma_map_page(xprt->sc_cm_id->device, page, 0, PAGE_SIZE, DMA_FROM_DEVICE); + if (ib_dma_mapping_error(xprt->sc_cm_id->device, pa)) + goto err_put_ctxt; + atomic_inc(&xprt->sc_dma_used); ctxt->sge[sge_no].addr = pa; ctxt->sge[sge_no].length = PAGE_SIZE; - ctxt->sge[sge_no].lkey = xprt->sc_phys_mr->lkey; + ctxt->sge[sge_no].lkey = xprt->sc_dma_lkey; buflen += PAGE_SIZE; } ctxt->count = sge_no; @@ -496,6 +524,10 @@ int svc_rdma_post_recv(struct svcxprt_rdma *xprt) svc_rdma_put_context(ctxt, 1); } return ret; + + err_put_ctxt: + svc_rdma_put_context(ctxt, 1); + return -ENOMEM; } /* @@ -566,7 +598,7 @@ static int rdma_listen_handler(struct rdma_cm_id *cma_id, dprintk("svcrdma: Connect request on cma_id=%p, xprt = %p, " "event=%d\n", cma_id, cma_id->context, event->event); handle_connect_req(cma_id, - event->param.conn.responder_resources); + event->param.conn.initiator_depth); break; case RDMA_CM_EVENT_ESTABLISHED: @@ -686,6 +718,97 @@ static struct svc_xprt *svc_rdma_create(struct svc_serv *serv, return ERR_PTR(ret); } +static struct svc_rdma_fastreg_mr *rdma_alloc_frmr(struct svcxprt_rdma *xprt) +{ + struct ib_mr *mr; + struct ib_fast_reg_page_list *pl; + struct svc_rdma_fastreg_mr *frmr; + + frmr = kmalloc(sizeof(*frmr), GFP_KERNEL); + if (!frmr) + goto err; + + mr = ib_alloc_fast_reg_mr(xprt->sc_pd, RPCSVC_MAXPAGES); + if (!mr) + goto err_free_frmr; + + pl = ib_alloc_fast_reg_page_list(xprt->sc_cm_id->device, + RPCSVC_MAXPAGES); + if (!pl) + goto err_free_mr; + + frmr->mr = mr; + frmr->page_list = pl; + INIT_LIST_HEAD(&frmr->frmr_list); + return frmr; + + err_free_mr: + ib_dereg_mr(mr); + err_free_frmr: + kfree(frmr); + err: + return ERR_PTR(-ENOMEM); +} + +static void rdma_dealloc_frmr_q(struct svcxprt_rdma *xprt) +{ + struct svc_rdma_fastreg_mr *frmr; + + while (!list_empty(&xprt->sc_frmr_q)) { + frmr = list_entry(xprt->sc_frmr_q.next, + struct svc_rdma_fastreg_mr, frmr_list); + list_del_init(&frmr->frmr_list); + ib_dereg_mr(frmr->mr); + ib_free_fast_reg_page_list(frmr->page_list); + kfree(frmr); + } +} + +struct svc_rdma_fastreg_mr *svc_rdma_get_frmr(struct svcxprt_rdma *rdma) +{ + struct svc_rdma_fastreg_mr *frmr = NULL; + + spin_lock_bh(&rdma->sc_frmr_q_lock); + if (!list_empty(&rdma->sc_frmr_q)) { + frmr = list_entry(rdma->sc_frmr_q.next, + struct svc_rdma_fastreg_mr, frmr_list); + list_del_init(&frmr->frmr_list); + frmr->map_len = 0; + frmr->page_list_len = 0; + } + spin_unlock_bh(&rdma->sc_frmr_q_lock); + if (frmr) + return frmr; + + return rdma_alloc_frmr(rdma); +} + +static void frmr_unmap_dma(struct svcxprt_rdma *xprt, + struct svc_rdma_fastreg_mr *frmr) +{ + int page_no; + for (page_no = 0; page_no < frmr->page_list_len; page_no++) { + dma_addr_t addr = frmr->page_list->page_list[page_no]; + if (ib_dma_mapping_error(frmr->mr->device, addr)) + continue; + atomic_dec(&xprt->sc_dma_used); + ib_dma_unmap_single(frmr->mr->device, addr, PAGE_SIZE, + frmr->direction); + } +} + +void svc_rdma_put_frmr(struct svcxprt_rdma *rdma, + struct svc_rdma_fastreg_mr *frmr) +{ + if (frmr) { + frmr_unmap_dma(rdma, frmr); + spin_lock_bh(&rdma->sc_frmr_q_lock); + BUG_ON(!list_empty(&frmr->frmr_list)); + list_add(&frmr->frmr_list, &rdma->sc_frmr_q); + spin_unlock_bh(&rdma->sc_frmr_q_lock); + } +} + /* * This is the xpo_recvfrom function for listening endpoints. Its * purpose is to accept incoming connections. The CMA callback handler @@ -704,6 +827,8 @@ static struct svc_xprt *svc_rdma_accept(struct svc_xprt *xprt) struct rdma_conn_param conn_param; struct ib_qp_init_attr qp_attr; struct ib_device_attr devattr; + int dma_mr_acc; + int need_dma_mr; int ret; int i; @@ -819,15 +944,77 @@ static struct svc_xprt *svc_rdma_accept(struct svc_xprt *xprt) } newxprt->sc_qp = newxprt->sc_cm_id->qp; - /* Register all of physical memory */ - newxprt->sc_phys_mr = ib_get_dma_mr(newxprt->sc_pd, - IB_ACCESS_LOCAL_WRITE | - IB_ACCESS_REMOTE_WRITE); - if (IS_ERR(newxprt->sc_phys_mr)) { - dprintk("svcrdma: Failed to create DMA MR ret=%d\n", ret); + /* + * Use the most secure set of MR resources based on the + * transport type and available memory management features in + * the device. Here's the table implemented below: + * + * Fast Global DMA Remote WR + * Reg LKEY MR Access + * Sup'd Sup'd Needed Needed + * + * IWARP N N Y Y + * N Y Y Y + * Y N Y N + * Y Y N - + * + * IB N N Y N + * N Y N - + * Y N Y N + * Y Y N - + * + * NB: iWARP requires remote write access for the data sink + * of an RDMA_READ. IB does not. + */ + if (devattr.device_cap_flags & IB_DEVICE_MEM_MGT_EXTENSIONS) { + newxprt->sc_frmr_pg_list_len = + devattr.max_fast_reg_page_list_len; + newxprt->sc_dev_caps |= SVCRDMA_DEVCAP_FAST_REG; + } + + /* + * Determine if a DMA MR is required and if so, what privs are required + */ + switch (rdma_node_get_transport(newxprt->sc_cm_id->device->node_type)) { + case RDMA_TRANSPORT_IWARP: + newxprt->sc_dev_caps |= SVCRDMA_DEVCAP_READ_W_INV; + if (!(newxprt->sc_dev_caps & SVCRDMA_DEVCAP_FAST_REG)) { + need_dma_mr = 1; + dma_mr_acc = + (IB_ACCESS_LOCAL_WRITE | + IB_ACCESS_REMOTE_WRITE); + } else if (!(devattr.device_cap_flags & IB_DEVICE_LOCAL_DMA_LKEY)) { + need_dma_mr = 1; + dma_mr_acc = IB_ACCESS_LOCAL_WRITE; + } else + need_dma_mr = 0; + break; + case RDMA_TRANSPORT_IB: + if (!(devattr.device_cap_flags & IB_DEVICE_LOCAL_DMA_LKEY)) { + need_dma_mr = 1; + dma_mr_acc = IB_ACCESS_LOCAL_WRITE; + } else + need_dma_mr = 0; + break; + default: goto errout; } + /* Create the DMA MR if needed, otherwise, use the DMA LKEY */ + if (need_dma_mr) { + /* Register all of physical memory */ + newxprt->sc_phys_mr = + ib_get_dma_mr(newxprt->sc_pd, dma_mr_acc); + if (IS_ERR(newxprt->sc_phys_mr)) { + dprintk("svcrdma: Failed to create DMA MR ret=%d\n", + ret); + goto errout; + } + newxprt->sc_dma_lkey = newxprt->sc_phys_mr->lkey; + } else + newxprt->sc_dma_lkey = + newxprt->sc_cm_id->device->local_dma_lkey; + /* Post receive buffers */ for (i = 0; i < newxprt->sc_max_requests; i++) { ret = svc_rdma_post_recv(newxprt); @@ -961,6 +1148,9 @@ static void __svc_rdma_free(struct work_struct *work) WARN_ON(atomic_read(&rdma->sc_ctxt_used) != 0); WARN_ON(atomic_read(&rdma->sc_dma_used) != 0); + /* De-allocate fastreg mr */ + rdma_dealloc_frmr_q(rdma); + /* Destroy the QP if present (not a listener) */ if (rdma->sc_qp && !IS_ERR(rdma->sc_qp)) ib_destroy_qp(rdma->sc_qp); @@ -1014,21 +1204,59 @@ static int svc_rdma_has_wspace(struct svc_xprt *xprt) return 1; } +/* + * Attempt to register the kvec representing the RPC memory with the + * device. + * + * Returns: + * NULL : The device does not support fastreg or there were no more + * fastreg mr. + * frmr : The kvec register request was successfully posted. + * <0 : An error was encountered attempting to register the kvec. + */ +int svc_rdma_fastreg(struct svcxprt_rdma *xprt, + struct svc_rdma_fastreg_mr *frmr) +{ + struct ib_send_wr fastreg_wr; + u8 key; + + /* Bump the key */ + key = (u8)(frmr->mr->lkey & 0x000000FF); + ib_update_fast_reg_key(frmr->mr, ++key); + + /* Prepare FASTREG WR */ + memset(&fastreg_wr, 0, sizeof fastreg_wr); + fastreg_wr.opcode = IB_WR_FAST_REG_MR; + fastreg_wr.send_flags = IB_SEND_SIGNALED; + fastreg_wr.wr.fast_reg.iova_start = (unsigned long)frmr->kva; + fastreg_wr.wr.fast_reg.page_list = frmr->page_list; + fastreg_wr.wr.fast_reg.page_list_len = frmr->page_list_len; + fastreg_wr.wr.fast_reg.page_shift = PAGE_SHIFT; + fastreg_wr.wr.fast_reg.length = frmr->map_len; + fastreg_wr.wr.fast_reg.access_flags = frmr->access_flags; + fastreg_wr.wr.fast_reg.rkey = frmr->mr->lkey; + return svc_rdma_send(xprt, &fastreg_wr); +} + int svc_rdma_send(struct svcxprt_rdma *xprt, struct ib_send_wr *wr) { - struct ib_send_wr *bad_wr; + struct ib_send_wr *bad_wr, *n_wr; + int wr_count; + int i; int ret; if (test_bit(XPT_CLOSE, &xprt->sc_xprt.xpt_flags)) return -ENOTCONN; BUG_ON(wr->send_flags != IB_SEND_SIGNALED); - BUG_ON(((struct svc_rdma_op_ctxt *)(unsigned long)wr->wr_id)->wr_op != - wr->opcode); + wr_count = 1; + for (n_wr = wr->next; n_wr; n_wr = n_wr->next) + wr_count++; + /* If the SQ is full, wait until an SQ entry is available */ while (1) { spin_lock_bh(&xprt->sc_lock); - if (xprt->sc_sq_depth == atomic_read(&xprt->sc_sq_count)) { + if (xprt->sc_sq_depth < atomic_read(&xprt->sc_sq_count) + wr_count) { spin_unlock_bh(&xprt->sc_lock); atomic_inc(&rdma_stat_sq_starve); @@ -1043,19 +1271,26 @@ int svc_rdma_send(struct svcxprt_rdma *xprt, struct ib_send_wr *wr) return 0; continue; } - /* Bumped used SQ WR count and post */ - svc_xprt_get(&xprt->sc_xprt); + /* Take a transport ref for each WR posted */ + for (i = 0; i < wr_count; i++) + svc_xprt_get(&xprt->sc_xprt); + + /* Bump used SQ WR count and post */ + atomic_add(wr_count, &xprt->sc_sq_count); ret = ib_post_send(xprt->sc_qp, wr, &bad_wr); - if (!ret) - atomic_inc(&xprt->sc_sq_count); - else { - svc_xprt_put(&xprt->sc_xprt); + if (ret) { + set_bit(XPT_CLOSE, &xprt->sc_xprt.xpt_flags); + atomic_sub(wr_count, &xprt->sc_sq_count); + for (i = 0; i < wr_count; i ++) + svc_xprt_put(&xprt->sc_xprt); dprintk("svcrdma: failed to post SQ WR rc=%d, " "sc_sq_count=%d, sc_sq_depth=%d\n", ret, atomic_read(&xprt->sc_sq_count), xprt->sc_sq_depth); } spin_unlock_bh(&xprt->sc_lock); + if (ret) + wake_up(&xprt->sc_send_wait); break; } return ret; @@ -1079,10 +1314,14 @@ void svc_rdma_send_error(struct svcxprt_rdma *xprt, struct rpcrdma_msg *rmsgp, length = svc_rdma_xdr_encode_error(xprt, rmsgp, err, va); /* Prepare SGE for local address */ - atomic_inc(&xprt->sc_dma_used); sge.addr = ib_dma_map_page(xprt->sc_cm_id->device, p, 0, PAGE_SIZE, DMA_FROM_DEVICE); - sge.lkey = xprt->sc_phys_mr->lkey; + if (ib_dma_mapping_error(xprt->sc_cm_id->device, sge.addr)) { + put_page(p); + return; + } + atomic_inc(&xprt->sc_dma_used); + sge.lkey = xprt->sc_dma_lkey; sge.length = length; ctxt = svc_rdma_get_context(xprt); @@ -1103,6 +1342,9 @@ void svc_rdma_send_error(struct svcxprt_rdma *xprt, struct rpcrdma_msg *rmsgp, if (ret) { dprintk("svcrdma: Error %d posting send for protocol error\n", ret); + ib_dma_unmap_page(xprt->sc_cm_id->device, + sge.addr, PAGE_SIZE, + DMA_FROM_DEVICE); svc_rdma_put_context(ctxt, 1); } } diff --git a/net/sunrpc/xprtsock.c b/net/sunrpc/xprtsock.c index 4486c59c3aca..9a288d5eea64 100644 --- a/net/sunrpc/xprtsock.c +++ b/net/sunrpc/xprtsock.c @@ -3,8 +3,8 @@ * * Client-side transport implementation for sockets. * - * TCP callback races fixes (C) 1998 Red Hat Software <alan@redhat.com> - * TCP send fixes (C) 1998 Red Hat Software <alan@redhat.com> + * TCP callback races fixes (C) 1998 Red Hat + * TCP send fixes (C) 1998 Red Hat * TCP NFS related read + write fixes * (C) 1999 Dave Airlie, University of Limerick, Ireland <airlied@linux.ie> * diff --git a/net/unix/af_unix.c b/net/unix/af_unix.c index 015606b54d9b..c647aab8d418 100644 --- a/net/unix/af_unix.c +++ b/net/unix/af_unix.c @@ -1,7 +1,7 @@ /* * NET4: Implementation of BSD Unix domain sockets. * - * Authors: Alan Cox, <alan.cox@linux.org> + * Authors: Alan Cox, <alan@lxorguk.ukuu.org.uk> * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License diff --git a/scripts/bootgraph.pl b/scripts/bootgraph.pl new file mode 100644 index 000000000000..2243353fe55d --- /dev/null +++ b/scripts/bootgraph.pl @@ -0,0 +1,147 @@ +#!/usr/bin/perl + +# Copyright 2008, Intel Corporation +# +# This file is part of the Linux kernel +# +# This program file 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 program is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +# for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program in a file named COPYING; if not, write to the +# Free Software Foundation, Inc., +# 51 Franklin Street, Fifth Floor, +# Boston, MA 02110-1301 USA +# +# Authors: +# Arjan van de Ven <arjan@linux.intel.com> + + +# +# This script turns a dmesg output into a SVG graphic that shows which +# functions take how much time. You can view SVG graphics with various +# programs, including Inkscape, The Gimp and Firefox. +# +# +# For this script to work, the kernel needs to be compiled with the +# CONFIG_PRINTK_TIME configuration option enabled, and with +# "initcall_debug" passed on the kernel command line. +# +# usage: +# dmesg | perl scripts/bootgraph.pl > output.svg +# + +my @rows; +my %start, %end, %row; +my $done = 0; +my $rowcount = 0; +my $maxtime = 0; +my $firsttime = 100; +my $count = 0; +while (<>) { + my $line = $_; + if ($line =~ /([0-9\.]+)\] calling ([a-zA-Z0-9\_]+)\+/) { + my $func = $2; + if ($done == 0) { + $start{$func} = $1; + if ($1 < $firsttime) { + $firsttime = $1; + } + } + $row{$func} = 1; + if ($line =~ /\@ ([0-9]+)/) { + my $pid = $1; + if (!defined($rows[$pid])) { + $rowcount = $rowcount + 1; + $rows[$pid] = $rowcount; + } + $row{$func} = $rows[$pid]; + } + $count = $count + 1; + } + + if ($line =~ /([0-9\.]+)\] initcall ([a-zA-Z0-9\_]+)\+.*returned/) { + if ($done == 0) { + $end{$2} = $1; + $maxtime = $1; + } + } + if ($line =~ /Write protecting the/) { + $done = 1; + } + if ($line =~ /Freeing unused kernel memory/) { + $done = 1; + } +} + +if ($count == 0) { + print "No data found in the dmesg. Make sure that 'printk.time=1' and\n"; + print "'initcall_debug' are passed on the kernel command line.\n\n"; + print "Usage: \n"; + print " dmesg | perl scripts/bootgraph.pl > output.svg\n\n"; + exit; +} + +print "<?xml version=\"1.0\" standalone=\"no\"?> \n"; +print "<svg width=\"1000\" height=\"100%\" version=\"1.1\" xmlns=\"http://www.w3.org/2000/svg\">\n"; + +my @styles; + +$styles[0] = "fill:rgb(0,0,255);fill-opacity:0.5;stroke-width:1;stroke:rgb(0,0,0)"; +$styles[1] = "fill:rgb(0,255,0);fill-opacity:0.5;stroke-width:1;stroke:rgb(0,0,0)"; +$styles[2] = "fill:rgb(255,0,20);fill-opacity:0.5;stroke-width:1;stroke:rgb(0,0,0)"; +$styles[3] = "fill:rgb(255,255,20);fill-opacity:0.5;stroke-width:1;stroke:rgb(0,0,0)"; +$styles[4] = "fill:rgb(255,0,255);fill-opacity:0.5;stroke-width:1;stroke:rgb(0,0,0)"; +$styles[5] = "fill:rgb(0,255,255);fill-opacity:0.5;stroke-width:1;stroke:rgb(0,0,0)"; +$styles[6] = "fill:rgb(0,128,255);fill-opacity:0.5;stroke-width:1;stroke:rgb(0,0,0)"; +$styles[7] = "fill:rgb(0,255,128);fill-opacity:0.5;stroke-width:1;stroke:rgb(0,0,0)"; +$styles[8] = "fill:rgb(255,0,128);fill-opacity:0.5;stroke-width:1;stroke:rgb(0,0,0)"; +$styles[9] = "fill:rgb(255,255,128);fill-opacity:0.5;stroke-width:1;stroke:rgb(0,0,0)"; +$styles[10] = "fill:rgb(255,128,255);fill-opacity:0.5;stroke-width:1;stroke:rgb(0,0,0)"; +$styles[11] = "fill:rgb(128,255,255);fill-opacity:0.5;stroke-width:1;stroke:rgb(0,0,0)"; + +my $mult = 950.0 / ($maxtime - $firsttime); +my $threshold = ($maxtime - $firsttime) / 60.0; +my $stylecounter = 0; +while (($key,$value) = each %start) { + my $duration = $end{$key} - $start{$key}; + + if ($duration >= $threshold) { + my $s, $s2, $e, $y; + $s = ($value - $firsttime) * $mult; + $s2 = $s + 6; + $e = ($end{$key} - $firsttime) * $mult; + $w = $e - $s; + + $y = $row{$key} * 150; + $y2 = $y + 4; + + $style = $styles[$stylecounter]; + $stylecounter = $stylecounter + 1; + if ($stylecounter > 11) { + $stylecounter = 0; + }; + + print "<rect x=\"$s\" width=\"$w\" y=\"$y\" height=\"145\" style=\"$style\"/>\n"; + print "<text transform=\"translate($s2,$y2) rotate(90)\">$key</text>\n"; + } +} + + +# print the time line on top +my $time = $firsttime; +my $step = ($maxtime - $firsttime) / 15; +while ($time < $maxtime) { + my $s2 = ($time - $firsttime) * $mult; + my $tm = int($time * 100) / 100.0; + print "<text transform=\"translate($s2,89) rotate(90)\">$tm</text>\n"; + $time = $time + $step; +} + +print "</svg>\n"; diff --git a/scripts/mod/file2alias.c b/scripts/mod/file2alias.c index 473f94e56ead..d4dc222a74f3 100644 --- a/scripts/mod/file2alias.c +++ b/scripts/mod/file2alias.c @@ -206,6 +206,20 @@ static void do_usb_table(void *symval, unsigned long size, do_usb_entry_multi(symval + i, mod); } +/* Looks like: hid:bNvNpN */ +static int do_hid_entry(const char *filename, + struct hid_device_id *id, char *alias) +{ + id->vendor = TO_NATIVE(id->vendor); + id->product = TO_NATIVE(id->product); + + sprintf(alias, "hid:b%04X", id->bus); + ADD(alias, "v", id->vendor != HID_ANY_ID, id->vendor); + ADD(alias, "p", id->product != HID_ANY_ID, id->product); + + return 1; +} + /* Looks like: ieee1394:venNmoNspNverN */ static int do_ieee1394_entry(const char *filename, struct ieee1394_device_id *id, char *alias) @@ -745,6 +759,10 @@ void handle_moddevtable(struct module *mod, struct elf_info *info, else if (sym_is(symname, "__mod_usb_device_table")) /* special case to handle bcdDevice ranges */ do_usb_table(symval, sym->st_size, mod); + else if (sym_is(symname, "__mod_hid_device_table")) + do_table(symval, sym->st_size, + sizeof(struct hid_device_id), "hid", + do_hid_entry, mod); else if (sym_is(symname, "__mod_ieee1394_device_table")) do_table(symval, sym->st_size, sizeof(struct ieee1394_device_id), "ieee1394", |