diff options
Diffstat (limited to 'drivers')
117 files changed, 11305 insertions, 3736 deletions
diff --git a/drivers/acpi/glue.c b/drivers/acpi/glue.c index 3c578ef78c4..24649ada08d 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 49f274197b1..432cf401829 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 57029fefd64..a0f7ffb6808 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 cacf89e65af..da64108de77 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 275dc522c73..b09e43e7413 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 00000000000..ebca00e6c10 --- /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 00000000000..fd7f896b34f --- /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 00000000000..12c8a9ba6ed --- /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 00000000000..38517a117df --- /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 00000000000..b833b9769ab --- /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 00000000000..a54d4096e0f --- /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 426ac5add58..8a7d9dbb4d0 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 00000000000..5d69d27b935 --- /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 00000000000..1a0d0dfc62f --- /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 00000000000..e148f86fb58 --- /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 00000000000..deb42f931b7 --- /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 00000000000..ac5120f542c --- /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 00000000000..aad9ed1b406 --- /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 16feea01449..00000000000 --- 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 1b2e8dc3398..7f183b7147e 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 00000000000..406d8c82abf --- /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 00000000000..27ae750ca87 --- /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 d469bd0061c..4e6dc6e2652 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 4b7ab6a46d9..51aff08e10c 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 00000000000..d718b1607d0 --- /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 00000000000..f3a85a065f1 --- /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 00000000000..10945fe12d5 --- /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 9eb83cf9d22..acd81558618 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 00000000000..15f3c049245 --- /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 00000000000..3af8095a7de --- /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 00000000000..5ba68f7dbb7 --- /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 144578b1a00..1b7cba0f7e1 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 5a688274f6a..ea82f3718b2 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 c40f0403eda..497e0d1dd3c 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 18f09104765..5d9aa95fc3e 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 00a7b709019..1329ecb37a1 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 27fe4d8912c..1d3b8a394d4 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 1d0dac52f16..00000000000 --- 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 011326178c0..484e3eec2f8 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 b15f8824963..47ebe045f9b 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 842e9edb888..babd65dd46a 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 62d2d7c925b..abedb13c623 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 0caaafe0184..b342926dd7f 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 35689ef172c..72ab4b26809 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 daa9d422033..82ec6b1b646 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 0d395979b2d..bfe49243f38 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 e92b1ba4b45..c2f51cc5076 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 e7462924b50..875fabe16e3 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 07d92c11b5d..2281b5098e9 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 0dae245c625..5eff8ad834d 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 6abebe36441..759b1fe1c89 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 00000000000..25a7a5d08bc --- /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 00000000000..ebf99bef392 --- /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 00000000000..8dfe21bb3bd --- /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 00000000000..974678db22c --- /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 00000000000..6a0cedb5bb8 --- /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 5ba4bab6d43..7d15e7c6bca 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 e4e3241628d..a0f8b6e2d0a 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 e9d529442b0..1d8af334833 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 a0b4c851607..735fc947640 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 455ef529cd6..bc8e2413abd 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 744fac0b161..5c3c05da4d9 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 593fb643a61..e312d315a42 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 6ad92405d9a..1d8d46eb3c9 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 45e92164c26..47e53769af5 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 3e8d5faec3a..b19e4376ba7 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 f31985df0bb..1ace41a13ac 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 0f6fd63b284..265aa8a15af 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 7a379138b5a..d514e5019df 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 b75ddd8777f..0d9b0e6dccf 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 b2c5314582a..4407ac9bb55 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 42ce65f76a8..fd3eb07e3f4 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 4c4d6e877ea..3b5517b8fbd 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 87919419b70..c6480be0bc1 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 917970ed24a..852c399a8b0 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 4da5b09b9bc..968f64be374 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 0a21cfbd2b2..be55e9ae74d 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 29db711303b..bb8698a8675 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 306c2dc4ab3..33f956bd6b5 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 ffdc0a1892b..9d7786937aa 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 795c594a4b7..b751c1b96cf 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 f3a47a87dbb..180e968dc54 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 95e3464068d..f02764725a2 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 156f159aafb..81c6cdc3851 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 51ad3765e07..85587a6667b 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 0b28d0d8ffa..c235cdba69c 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 54df34f21c5..448cd40aeba 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 6671e2da0d5..d0ed1ef284a 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 17162748005..f11e900b437 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 c37ea436c91..38116f9d416 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 297877b68c4..4b2caa6b7ac 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 ec871f64676..c41d6876136 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 fa73e6eed6b..ed50d288e49 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 f54c45049d5..124d5d690dd 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 656200472fa..8e46a513a25 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 ad20f96edfa..2dced383bcf 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 243bd8d918f..ccd9cd35ecb 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 4f372396c51..85b3e785d48 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 136220b5ca8..e939a73ff79 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 b33ac47dd8d..44d31bbf39e 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 a656128f1fd..4dada6ee111 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 ac2c64efe65..254d40c02ee 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 263699d6152..366565aba86 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 9c798626156..02a774424e8 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 00000000000..3688e339db8 --- /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 00000000000..1f44b17e23b --- /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 00000000000..48b372e038a --- /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 b23af0c2a86..963ad0b6a4e 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 8f83fc994f5..f5e618562c5 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 d3b211af4e1..403ecad48d4 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 cdc710ea00f..00000000000 --- 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 f5732d8f67f..21fe07f9df8 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 d4104a3bbe8..d3ca7d32abe 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 9cbff84b787..da91bb16da8 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 9901064199b..dd3eaaad444 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 4055dbdd1b4..491c1c1baf4 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 bd1f57b259d..6df29a62d72 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; |