add idl4k kernel firmware version 1.13.0.105

This commit is contained in:
Jaroslav Kysela
2015-03-26 17:22:37 +01:00
parent 5194d2792e
commit e9070cdc77
31064 changed files with 12769984 additions and 0 deletions

View File

@@ -0,0 +1,330 @@
#
# Input misc drivers configuration
#
menuconfig INPUT_MISC
bool "Miscellaneous devices"
help
Say Y here, and a list of miscellaneous input drivers will be displayed.
Everything that didn't fit into the other categories is here. This option
doesn't affect the kernel.
If unsure, say Y.
if INPUT_MISC
config INPUT_PCSPKR
tristate "PC Speaker support"
depends on PCSPKR_PLATFORM
help
Say Y here if you want the standard PC Speaker to be used for
bells and whistles.
If unsure, say Y.
To compile this driver as a module, choose M here: the
module will be called pcspkr.
config INPUT_SPARCSPKR
tristate "SPARC Speaker support"
depends on PCI && SPARC64
help
Say Y here if you want the standard Speaker on Sparc PCI systems
to be used for bells and whistles.
If unsure, say Y.
To compile this driver as a module, choose M here: the
module will be called sparcspkr.
config INPUT_M68K_BEEP
tristate "M68k Beeper support"
depends on M68K
config INPUT_APANEL
tristate "Fujitsu Lifebook Application Panel buttons"
depends on X86 && I2C && LEDS_CLASS
select INPUT_POLLDEV
select CHECK_SIGNATURE
help
Say Y here for support of the Application Panel buttons, used on
Fujitsu Lifebook. These are attached to the mainboard through
an SMBus interface managed by the I2C Intel ICH (i801) driver,
which you should also build for this kernel.
To compile this driver as a module, choose M here: the module will
be called apanel.
config INPUT_IXP4XX_BEEPER
tristate "IXP4XX Beeper support"
depends on ARCH_IXP4XX
help
If you say yes here, you can connect a beeper to the
ixp4xx gpio pins. This is used by the LinkSys NSLU2.
If unsure, say Y.
To compile this driver as a module, choose M here: the
module will be called ixp4xx-beeper.
config INPUT_COBALT_BTNS
tristate "Cobalt button interface"
depends on MIPS_COBALT
select INPUT_POLLDEV
help
Say Y here if you want to support MIPS Cobalt button interface.
To compile this driver as a module, choose M here: the
module will be called cobalt_btns.
config INPUT_WISTRON_BTNS
tristate "x86 Wistron laptop button interface"
depends on X86 && !X86_64
select INPUT_POLLDEV
select NEW_LEDS
select LEDS_CLASS
select CHECK_SIGNATURE
help
Say Y here for support of Wistron laptop button interfaces, used on
laptops of various brands, including Acer and Fujitsu-Siemens. If
available, mail and wifi LEDs will be controllable via /sys/class/leds.
To compile this driver as a module, choose M here: the module will
be called wistron_btns.
config INPUT_ATLAS_BTNS
tristate "x86 Atlas button interface"
depends on X86 && ACPI
help
Say Y here for support of Atlas wallmount touchscreen buttons.
The events will show up as scancodes F1 through F9 via evdev.
To compile this driver as a module, choose M here: the module will
be called atlas_btns.
config INPUT_ATI_REMOTE
tristate "ATI / X10 USB RF remote control"
depends on USB_ARCH_HAS_HCD
select USB
help
Say Y here if you want to use an ATI or X10 "Lola" USB remote control.
These are RF remotes with USB receivers.
The ATI remote comes with many of ATI's All-In-Wonder video cards.
The X10 "Lola" remote is available at:
<http://www.x10.com/products/lola_sg1.htm>
This driver provides mouse pointer, left and right mouse buttons,
and maps all the other remote buttons to keypress events.
To compile this driver as a module, choose M here: the module will be
called ati_remote.
config INPUT_ATI_REMOTE2
tristate "ATI / Philips USB RF remote control"
depends on USB_ARCH_HAS_HCD
select USB
help
Say Y here if you want to use an ATI or Philips USB RF remote control.
These are RF remotes with USB receivers.
ATI Remote Wonder II comes with some ATI's All-In-Wonder video cards
and is also available as a separate product.
This driver provides mouse pointer, left and right mouse buttons,
and maps all the other remote buttons to keypress events.
To compile this driver as a module, choose M here: the module will be
called ati_remote2.
config INPUT_KEYSPAN_REMOTE
tristate "Keyspan DMR USB remote control (EXPERIMENTAL)"
depends on EXPERIMENTAL
depends on USB_ARCH_HAS_HCD
select USB
help
Say Y here if you want to use a Keyspan DMR USB remote control.
Currently only the UIA-11 type of receiver has been tested. The tag
on the receiver that connects to the USB port should have a P/N that
will tell you what type of DMR you have. The UIA-10 type is not
supported at this time. This driver maps all buttons to keypress
events.
To compile this driver as a module, choose M here: the module will
be called keyspan_remote.
config INPUT_POWERMATE
tristate "Griffin PowerMate and Contour Jog support"
depends on USB_ARCH_HAS_HCD
select USB
help
Say Y here if you want to use Griffin PowerMate or Contour Jog devices.
These are aluminum dials which can measure clockwise and anticlockwise
rotation. The dial also acts as a pushbutton. The base contains an LED
which can be instructed to pulse or to switch to a particular intensity.
You can download userspace tools from
<http://sowerbutts.com/powermate/>.
To compile this driver as a module, choose M here: the
module will be called powermate.
config INPUT_YEALINK
tristate "Yealink usb-p1k voip phone"
depends on EXPERIMENTAL
depends on USB_ARCH_HAS_HCD
select USB
help
Say Y here if you want to enable keyboard and LCD functions of the
Yealink usb-p1k usb phones. The audio part is enabled by the generic
usb sound driver, so you might want to enable that as well.
For information about how to use these additional functions, see
<file:Documentation/input/yealink.txt>.
To compile this driver as a module, choose M here: the module will be
called yealink.
config INPUT_CM109
tristate "C-Media CM109 USB I/O Controller"
depends on EXPERIMENTAL
depends on USB_ARCH_HAS_HCD
select USB
help
Say Y here if you want to enable keyboard and buzzer functions of the
C-Media CM109 usb phones. The audio part is enabled by the generic
usb sound driver, so you might want to enable that as well.
To compile this driver as a module, choose M here: the module will be
called cm109.
config INPUT_TWL4030_PWRBUTTON
tristate "TWL4030 Power button Driver"
depends on TWL4030_CORE
help
Say Y here if you want to enable power key reporting via the
TWL4030 family of chips.
To compile this driver as a module, choose M here. The module will
be called twl4030_pwrbutton.
config INPUT_UINPUT
tristate "User level driver support"
help
Say Y here if you want to support user level drivers for input
subsystem accessible under char device 10:223 - /dev/input/uinput.
To compile this driver as a module, choose M here: the
module will be called uinput.
config INPUT_SGI_BTNS
tristate "SGI Indy/O2 volume button interface"
depends on SGI_IP22 || SGI_IP32
select INPUT_POLLDEV
help
Say Y here if you want to support SGI Indy/O2 volume button interface.
To compile this driver as a module, choose M here: the
module will be called sgi_btns.
config INPUT_WINBOND_CIR
tristate "Winbond IR remote control"
depends on X86 && PNP
select NEW_LEDS
select LEDS_CLASS
select LEDS_TRIGGERS
select BITREVERSE
help
Say Y here if you want to use the IR remote functionality found
in some Winbond SuperI/O chips. Currently only the WPCD376I
chip is supported (included in some Intel Media series motherboards).
IR Receive and wake-on-IR from suspend and power-off is currently
supported.
To compile this driver as a module, choose M here: the module will be
called winbond_cir.
config HP_SDC_RTC
tristate "HP SDC Real Time Clock"
depends on (GSC || HP300) && SERIO
select HP_SDC
help
Say Y here if you want to support the built-in real time clock
of the HP SDC controller.
config INPUT_PCF50633_PMU
tristate "PCF50633 PMU events"
depends on MFD_PCF50633
help
Say Y to include support for delivering PMU events via input
layer on NXP PCF50633.
config INPUT_GPIO_ROTARY_ENCODER
tristate "Rotary encoders connected to GPIO pins"
depends on GPIOLIB && GENERIC_GPIO
help
Say Y here to add support for rotary encoders connected to GPIO lines.
Check file:Documentation/input/rotary-encoder.txt for more
information.
To compile this driver as a module, choose M here: the
module will be called rotary_encoder.
config INPUT_RB532_BUTTON
tristate "Mikrotik Routerboard 532 button interface"
depends on MIKROTIK_RB532
depends on GPIOLIB && GENERIC_GPIO
select INPUT_POLLDEV
help
Say Y here if you want support for the S1 button built into
Mikrotik's Routerboard 532.
To compile this driver as a module, choose M here: the
module will be called rb532_button.
config INPUT_DM355EVM
tristate "TI DaVinci DM355 EVM Keypad and IR Remote"
depends on MFD_DM355EVM_MSP
help
Supports the pushbuttons and IR remote used with
the DM355 EVM board.
To compile this driver as a module, choose M here: the
module will be called dm355evm_keys.
config INPUT_BFIN_ROTARY
tristate "Blackfin Rotary support"
depends on BF54x || BF52x
help
Say Y here if you want to use the Blackfin Rotary.
To compile this driver as a module, choose M here: the
module will be called bfin-rotary.
config INPUT_WM831X_ON
tristate "WM831X ON pin"
depends on MFD_WM831X
help
Support the ON pin of WM831X PMICs as an input device
reporting power button status.
To compile this driver as a module, choose M here: the module
will be called wm831x_on.
config INPUT_PCAP
tristate "Motorola EZX PCAP misc input events"
depends on EZX_PCAP
help
Say Y here if you want to use Power key and Headphone button
on Motorola EZX phones.
To compile this driver as a module, choose M here: the
module will be called pcap_keys.
config INPUT_TM1668
tristate "TM1668 LED/Keyboard controller"
help
Say Y here if you want to enable driver for keyboard and LED
display controlled by GPIO-connected TM1668 chip. Note that
a related platform device must be defined in the board setup.
To compile this driver as a module, choose M here: the
module will be called tm1668.
endif

View File

@@ -0,0 +1,34 @@
#
# Makefile for the input misc drivers.
#
# Each configuration option enables a list of files.
obj-$(CONFIG_INPUT_APANEL) += apanel.o
obj-$(CONFIG_INPUT_ATI_REMOTE) += ati_remote.o
obj-$(CONFIG_INPUT_ATI_REMOTE2) += ati_remote2.o
obj-$(CONFIG_INPUT_ATLAS_BTNS) += atlas_btns.o
obj-$(CONFIG_INPUT_BFIN_ROTARY) += bfin_rotary.o
obj-$(CONFIG_INPUT_CM109) += cm109.o
obj-$(CONFIG_INPUT_COBALT_BTNS) += cobalt_btns.o
obj-$(CONFIG_INPUT_DM355EVM) += dm355evm_keys.o
obj-$(CONFIG_HP_SDC_RTC) += hp_sdc_rtc.o
obj-$(CONFIG_INPUT_IXP4XX_BEEPER) += ixp4xx-beeper.o
obj-$(CONFIG_INPUT_KEYSPAN_REMOTE) += keyspan_remote.o
obj-$(CONFIG_INPUT_M68K_BEEP) += m68kspkr.o
obj-$(CONFIG_INPUT_PCAP) += pcap_keys.o
obj-$(CONFIG_INPUT_PCF50633_PMU) += pcf50633-input.o
obj-$(CONFIG_INPUT_PCSPKR) += pcspkr.o
obj-$(CONFIG_INPUT_POWERMATE) += powermate.o
obj-$(CONFIG_INPUT_RB532_BUTTON) += rb532_button.o
obj-$(CONFIG_INPUT_GPIO_ROTARY_ENCODER) += rotary_encoder.o
obj-$(CONFIG_INPUT_SGI_BTNS) += sgi_btns.o
obj-$(CONFIG_INPUT_SPARCSPKR) += sparcspkr.o
obj-$(CONFIG_INPUT_TM1668) += tm1668.o
obj-$(CONFIG_INPUT_TWL4030_PWRBUTTON) += twl4030-pwrbutton.o
obj-$(CONFIG_INPUT_UINPUT) += uinput.o
obj-$(CONFIG_INPUT_WINBOND_CIR) += winbond-cir.o
obj-$(CONFIG_INPUT_WISTRON_BTNS) += wistron_btns.o
obj-$(CONFIG_INPUT_WM831X_ON) += wm831x-on.o
obj-$(CONFIG_INPUT_YEALINK) += yealink.o

View File

@@ -0,0 +1,350 @@
/*
* Fujitsu Lifebook Application Panel button drive
*
* Copyright (C) 2007 Stephen Hemminger <shemminger@linux-foundation.org>
* Copyright (C) 2001-2003 Jochen Eisinger <jochen@penguin-breeder.org>
*
* 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.
*
* Many Fujitsu Lifebook laptops have a small panel of buttons that are
* accessible via the i2c/smbus interface. This driver polls those
* buttons and generates input events.
*
* For more details see:
* http://apanel.sourceforge.net/tech.php
*/
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/ioport.h>
#include <linux/io.h>
#include <linux/input-polldev.h>
#include <linux/i2c.h>
#include <linux/workqueue.h>
#include <linux/leds.h>
#define APANEL_NAME "Fujitsu Application Panel"
#define APANEL_VERSION "1.3.1"
#define APANEL "apanel"
/* How often we poll keys - msecs */
#define POLL_INTERVAL_DEFAULT 1000
/* Magic constants in BIOS that tell about buttons */
enum apanel_devid {
APANEL_DEV_NONE = 0,
APANEL_DEV_APPBTN = 1,
APANEL_DEV_CDBTN = 2,
APANEL_DEV_LCD = 3,
APANEL_DEV_LED = 4,
APANEL_DEV_MAX,
};
enum apanel_chip {
CHIP_NONE = 0,
CHIP_OZ992C = 1,
CHIP_OZ163T = 2,
CHIP_OZ711M3 = 4,
};
/* Result of BIOS snooping/probing -- what features are supported */
static enum apanel_chip device_chip[APANEL_DEV_MAX];
#define MAX_PANEL_KEYS 12
struct apanel {
struct input_polled_dev *ipdev;
struct i2c_client *client;
unsigned short keymap[MAX_PANEL_KEYS];
u16 nkeys;
u16 led_bits;
struct work_struct led_work;
struct led_classdev mail_led;
};
static int apanel_probe(struct i2c_client *, const struct i2c_device_id *);
static void report_key(struct input_dev *input, unsigned keycode)
{
pr_debug(APANEL ": report key %#x\n", keycode);
input_report_key(input, keycode, 1);
input_sync(input);
input_report_key(input, keycode, 0);
input_sync(input);
}
/* Poll for key changes
*
* Read Application keys via SMI
* A (0x4), B (0x8), Internet (0x2), Email (0x1).
*
* CD keys:
* Forward (0x100), Rewind (0x200), Stop (0x400), Pause (0x800)
*/
static void apanel_poll(struct input_polled_dev *ipdev)
{
struct apanel *ap = ipdev->private;
struct input_dev *idev = ipdev->input;
u8 cmd = device_chip[APANEL_DEV_APPBTN] == CHIP_OZ992C ? 0 : 8;
s32 data;
int i;
data = i2c_smbus_read_word_data(ap->client, cmd);
if (data < 0)
return; /* ignore errors (due to ACPI??) */
/* write back to clear latch */
i2c_smbus_write_word_data(ap->client, cmd, 0);
if (!data)
return;
dev_dbg(&idev->dev, APANEL ": data %#x\n", data);
for (i = 0; i < idev->keycodemax; i++)
if ((1u << i) & data)
report_key(idev, ap->keymap[i]);
}
/* Track state changes of LED */
static void led_update(struct work_struct *work)
{
struct apanel *ap = container_of(work, struct apanel, led_work);
i2c_smbus_write_word_data(ap->client, 0x10, ap->led_bits);
}
static void mail_led_set(struct led_classdev *led,
enum led_brightness value)
{
struct apanel *ap = container_of(led, struct apanel, mail_led);
if (value != LED_OFF)
ap->led_bits |= 0x8000;
else
ap->led_bits &= ~0x8000;
schedule_work(&ap->led_work);
}
static int apanel_remove(struct i2c_client *client)
{
struct apanel *ap = i2c_get_clientdata(client);
if (device_chip[APANEL_DEV_LED] != CHIP_NONE)
led_classdev_unregister(&ap->mail_led);
input_unregister_polled_device(ap->ipdev);
input_free_polled_device(ap->ipdev);
return 0;
}
static void apanel_shutdown(struct i2c_client *client)
{
apanel_remove(client);
}
static struct i2c_device_id apanel_id[] = {
{ "fujitsu_apanel", 0 },
{ }
};
MODULE_DEVICE_TABLE(i2c, apanel_id);
static struct i2c_driver apanel_driver = {
.driver = {
.name = APANEL,
},
.probe = &apanel_probe,
.remove = &apanel_remove,
.shutdown = &apanel_shutdown,
.id_table = apanel_id,
};
static struct apanel apanel = {
.keymap = {
[0] = KEY_MAIL,
[1] = KEY_WWW,
[2] = KEY_PROG2,
[3] = KEY_PROG1,
[8] = KEY_FORWARD,
[9] = KEY_REWIND,
[10] = KEY_STOPCD,
[11] = KEY_PLAYPAUSE,
},
.mail_led = {
.name = "mail:blue",
.brightness_set = mail_led_set,
},
};
/* NB: Only one panel on the i2c. */
static int apanel_probe(struct i2c_client *client,
const struct i2c_device_id *id)
{
struct apanel *ap;
struct input_polled_dev *ipdev;
struct input_dev *idev;
u8 cmd = device_chip[APANEL_DEV_APPBTN] == CHIP_OZ992C ? 0 : 8;
int i, err = -ENOMEM;
ap = &apanel;
ipdev = input_allocate_polled_device();
if (!ipdev)
goto out1;
ap->ipdev = ipdev;
ap->client = client;
i2c_set_clientdata(client, ap);
err = i2c_smbus_write_word_data(client, cmd, 0);
if (err) {
dev_warn(&client->dev, APANEL ": smbus write error %d\n",
err);
goto out3;
}
ipdev->poll = apanel_poll;
ipdev->poll_interval = POLL_INTERVAL_DEFAULT;
ipdev->private = ap;
idev = ipdev->input;
idev->name = APANEL_NAME " buttons";
idev->phys = "apanel/input0";
idev->id.bustype = BUS_HOST;
idev->dev.parent = &client->dev;
set_bit(EV_KEY, idev->evbit);
idev->keycode = ap->keymap;
idev->keycodesize = sizeof(ap->keymap[0]);
idev->keycodemax = (device_chip[APANEL_DEV_CDBTN] != CHIP_NONE) ? 12 : 4;
for (i = 0; i < idev->keycodemax; i++)
if (ap->keymap[i])
set_bit(ap->keymap[i], idev->keybit);
err = input_register_polled_device(ipdev);
if (err)
goto out3;
INIT_WORK(&ap->led_work, led_update);
if (device_chip[APANEL_DEV_LED] != CHIP_NONE) {
err = led_classdev_register(&client->dev, &ap->mail_led);
if (err)
goto out4;
}
return 0;
out4:
input_unregister_polled_device(ipdev);
out3:
input_free_polled_device(ipdev);
out1:
return err;
}
/* Scan the system ROM for the signature "FJKEYINF" */
static __init const void __iomem *bios_signature(const void __iomem *bios)
{
ssize_t offset;
const unsigned char signature[] = "FJKEYINF";
for (offset = 0; offset < 0x10000; offset += 0x10) {
if (check_signature(bios + offset, signature,
sizeof(signature)-1))
return bios + offset;
}
pr_notice(APANEL ": Fujitsu BIOS signature '%s' not found...\n",
signature);
return NULL;
}
static int __init apanel_init(void)
{
void __iomem *bios;
const void __iomem *p;
u8 devno;
unsigned char i2c_addr;
int found = 0;
bios = ioremap(0xF0000, 0x10000); /* Can't fail */
p = bios_signature(bios);
if (!p) {
iounmap(bios);
return -ENODEV;
}
/* just use the first address */
p += 8;
i2c_addr = readb(p + 3) >> 1;
for ( ; (devno = readb(p)) & 0x7f; p += 4) {
unsigned char method, slave, chip;
method = readb(p + 1);
chip = readb(p + 2);
slave = readb(p + 3) >> 1;
if (slave != i2c_addr) {
pr_notice(APANEL ": only one SMBus slave "
"address supported, skiping device...\n");
continue;
}
/* translate alternative device numbers */
switch (devno) {
case 6:
devno = APANEL_DEV_APPBTN;
break;
case 7:
devno = APANEL_DEV_LED;
break;
}
if (devno >= APANEL_DEV_MAX)
pr_notice(APANEL ": unknown device %u found\n", devno);
else if (device_chip[devno] != CHIP_NONE)
pr_warning(APANEL ": duplicate entry for devno %u\n", devno);
else if (method != 1 && method != 2 && method != 4) {
pr_notice(APANEL ": unknown method %u for devno %u\n",
method, devno);
} else {
device_chip[devno] = (enum apanel_chip) chip;
++found;
}
}
iounmap(bios);
if (found == 0) {
pr_info(APANEL ": no input devices reported by BIOS\n");
return -EIO;
}
return i2c_add_driver(&apanel_driver);
}
module_init(apanel_init);
static void __exit apanel_cleanup(void)
{
i2c_del_driver(&apanel_driver);
}
module_exit(apanel_cleanup);
MODULE_AUTHOR("Stephen Hemminger <shemminger@linux-foundation.org>");
MODULE_DESCRIPTION(APANEL_NAME " driver");
MODULE_LICENSE("GPL");
MODULE_VERSION(APANEL_VERSION);
MODULE_ALIAS("dmi:*:svnFUJITSU:pnLifeBook*:pvr*:rvnFUJITSU:*");
MODULE_ALIAS("dmi:*:svnFUJITSU:pnLifebook*:pvr*:rvnFUJITSU:*");

View File

@@ -0,0 +1,863 @@
/*
* USB ATI Remote support
*
* Version 2.2.0 Copyright (c) 2004 Torrey Hoffman <thoffman@arnor.net>
* Version 2.1.1 Copyright (c) 2002 Vladimir Dergachev
*
* This 2.2.0 version is a rewrite / cleanup of the 2.1.1 driver, including
* porting to the 2.6 kernel interfaces, along with other modification
* to better match the style of the existing usb/input drivers. However, the
* protocol and hardware handling is essentially unchanged from 2.1.1.
*
* The 2.1.1 driver was derived from the usbati_remote and usbkbd drivers by
* Vojtech Pavlik.
*
* Changes:
*
* Feb 2004: Torrey Hoffman <thoffman@arnor.net>
* Version 2.2.0
* Jun 2004: Torrey Hoffman <thoffman@arnor.net>
* Version 2.2.1
* Added key repeat support contributed by:
* Vincent Vanackere <vanackere@lif.univ-mrs.fr>
* Added support for the "Lola" remote contributed by:
* Seth Cohn <sethcohn@yahoo.com>
*
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* 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
*
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
*
* Hardware & software notes
*
* These remote controls are distributed by ATI as part of their
* "All-In-Wonder" video card packages. The receiver self-identifies as a
* "USB Receiver" with manufacturer "X10 Wireless Technology Inc".
*
* The "Lola" remote is available from X10. See:
* http://www.x10.com/products/lola_sg1.htm
* The Lola is similar to the ATI remote but has no mouse support, and slightly
* different keys.
*
* It is possible to use multiple receivers and remotes on multiple computers
* simultaneously by configuring them to use specific channels.
*
* The RF protocol used by the remote supports 16 distinct channels, 1 to 16.
* Actually, it may even support more, at least in some revisions of the
* hardware.
*
* Each remote can be configured to transmit on one channel as follows:
* - Press and hold the "hand icon" button.
* - When the red LED starts to blink, let go of the "hand icon" button.
* - When it stops blinking, input the channel code as two digits, from 01
* to 16, and press the hand icon again.
*
* The timing can be a little tricky. Try loading the module with debug=1
* to have the kernel print out messages about the remote control number
* and mask. Note: debugging prints remote numbers as zero-based hexadecimal.
*
* The driver has a "channel_mask" parameter. This bitmask specifies which
* channels will be ignored by the module. To mask out channels, just add
* all the 2^channel_number values together.
*
* For instance, set channel_mask = 2^4 = 16 (binary 10000) to make ati_remote
* ignore signals coming from remote controls transmitting on channel 4, but
* accept all other channels.
*
* Or, set channel_mask = 65533, (0xFFFD), and all channels except 1 will be
* ignored.
*
* The default is 0 (respond to all channels). Bit 0 and bits 17-32 of this
* parameter are unused.
*
*/
#include <linux/kernel.h>
#include <linux/errno.h>
#include <linux/init.h>
#include <linux/slab.h>
#include <linux/module.h>
#include <linux/usb/input.h>
#include <linux/wait.h>
#include <linux/jiffies.h>
/*
* Module and Version Information, Module Parameters
*/
#define ATI_REMOTE_VENDOR_ID 0x0bc7
#define ATI_REMOTE_PRODUCT_ID 0x004
#define LOLA_REMOTE_PRODUCT_ID 0x002
#define MEDION_REMOTE_PRODUCT_ID 0x006
#define DRIVER_VERSION "2.2.1"
#define DRIVER_AUTHOR "Torrey Hoffman <thoffman@arnor.net>"
#define DRIVER_DESC "ATI/X10 RF USB Remote Control"
#define NAME_BUFSIZE 80 /* size of product name, path buffers */
#define DATA_BUFSIZE 63 /* size of URB data buffers */
/*
* Duplicate event filtering time.
* Sequential, identical KIND_FILTERED inputs with less than
* FILTER_TIME milliseconds between them are considered as repeat
* events. The hardware generates 5 events for the first keypress
* and we have to take this into account for an accurate repeat
* behaviour.
*/
#define FILTER_TIME 60 /* msec */
#define REPEAT_DELAY 500 /* msec */
static unsigned long channel_mask;
module_param(channel_mask, ulong, 0644);
MODULE_PARM_DESC(channel_mask, "Bitmask of remote control channels to ignore");
static int debug;
module_param(debug, int, 0644);
MODULE_PARM_DESC(debug, "Enable extra debug messages and information");
static int repeat_filter = FILTER_TIME;
module_param(repeat_filter, int, 0644);
MODULE_PARM_DESC(repeat_filter, "Repeat filter time, default = 60 msec");
static int repeat_delay = REPEAT_DELAY;
module_param(repeat_delay, int, 0644);
MODULE_PARM_DESC(repeat_delay, "Delay before sending repeats, default = 500 msec");
#define dbginfo(dev, format, arg...) do { if (debug) dev_info(dev , format , ## arg); } while (0)
#undef err
#define err(format, arg...) printk(KERN_ERR format , ## arg)
static struct usb_device_id ati_remote_table[] = {
{ USB_DEVICE(ATI_REMOTE_VENDOR_ID, ATI_REMOTE_PRODUCT_ID) },
{ USB_DEVICE(ATI_REMOTE_VENDOR_ID, LOLA_REMOTE_PRODUCT_ID) },
{ USB_DEVICE(ATI_REMOTE_VENDOR_ID, MEDION_REMOTE_PRODUCT_ID) },
{} /* Terminating entry */
};
MODULE_DEVICE_TABLE(usb, ati_remote_table);
/* Get hi and low bytes of a 16-bits int */
#define HI(a) ((unsigned char)((a) >> 8))
#define LO(a) ((unsigned char)((a) & 0xff))
#define SEND_FLAG_IN_PROGRESS 1
#define SEND_FLAG_COMPLETE 2
/* Device initialization strings */
static char init1[] = { 0x01, 0x00, 0x20, 0x14 };
static char init2[] = { 0x01, 0x00, 0x20, 0x14, 0x20, 0x20, 0x20 };
struct ati_remote {
struct input_dev *idev;
struct usb_device *udev;
struct usb_interface *interface;
struct urb *irq_urb;
struct urb *out_urb;
struct usb_endpoint_descriptor *endpoint_in;
struct usb_endpoint_descriptor *endpoint_out;
unsigned char *inbuf;
unsigned char *outbuf;
dma_addr_t inbuf_dma;
dma_addr_t outbuf_dma;
unsigned char old_data[2]; /* Detect duplicate events */
unsigned long old_jiffies;
unsigned long acc_jiffies; /* handle acceleration */
unsigned long first_jiffies;
unsigned int repeat_count;
char name[NAME_BUFSIZE];
char phys[NAME_BUFSIZE];
wait_queue_head_t wait;
int send_flags;
};
/* "Kinds" of messages sent from the hardware to the driver. */
#define KIND_END 0
#define KIND_LITERAL 1 /* Simply pass to input system */
#define KIND_FILTERED 2 /* Add artificial key-up events, drop keyrepeats */
#define KIND_LU 3 /* Directional keypad diagonals - left up, */
#define KIND_RU 4 /* right up, */
#define KIND_LD 5 /* left down, */
#define KIND_RD 6 /* right down */
#define KIND_ACCEL 7 /* Directional keypad - left, right, up, down.*/
/* Translation table from hardware messages to input events. */
static const struct {
short kind;
unsigned char data1, data2;
int type;
unsigned int code;
int value;
} ati_remote_tbl[] = {
/* Directional control pad axes */
{KIND_ACCEL, 0x35, 0x70, EV_REL, REL_X, -1}, /* left */
{KIND_ACCEL, 0x36, 0x71, EV_REL, REL_X, 1}, /* right */
{KIND_ACCEL, 0x37, 0x72, EV_REL, REL_Y, -1}, /* up */
{KIND_ACCEL, 0x38, 0x73, EV_REL, REL_Y, 1}, /* down */
/* Directional control pad diagonals */
{KIND_LU, 0x39, 0x74, EV_REL, 0, 0}, /* left up */
{KIND_RU, 0x3a, 0x75, EV_REL, 0, 0}, /* right up */
{KIND_LD, 0x3c, 0x77, EV_REL, 0, 0}, /* left down */
{KIND_RD, 0x3b, 0x76, EV_REL, 0, 0}, /* right down */
/* "Mouse button" buttons */
{KIND_LITERAL, 0x3d, 0x78, EV_KEY, BTN_LEFT, 1}, /* left btn down */
{KIND_LITERAL, 0x3e, 0x79, EV_KEY, BTN_LEFT, 0}, /* left btn up */
{KIND_LITERAL, 0x41, 0x7c, EV_KEY, BTN_RIGHT, 1},/* right btn down */
{KIND_LITERAL, 0x42, 0x7d, EV_KEY, BTN_RIGHT, 0},/* right btn up */
/* Artificial "doubleclick" events are generated by the hardware.
* They are mapped to the "side" and "extra" mouse buttons here. */
{KIND_FILTERED, 0x3f, 0x7a, EV_KEY, BTN_SIDE, 1}, /* left dblclick */
{KIND_FILTERED, 0x43, 0x7e, EV_KEY, BTN_EXTRA, 1},/* right dblclick */
/* keyboard. */
{KIND_FILTERED, 0xd2, 0x0d, EV_KEY, KEY_1, 1},
{KIND_FILTERED, 0xd3, 0x0e, EV_KEY, KEY_2, 1},
{KIND_FILTERED, 0xd4, 0x0f, EV_KEY, KEY_3, 1},
{KIND_FILTERED, 0xd5, 0x10, EV_KEY, KEY_4, 1},
{KIND_FILTERED, 0xd6, 0x11, EV_KEY, KEY_5, 1},
{KIND_FILTERED, 0xd7, 0x12, EV_KEY, KEY_6, 1},
{KIND_FILTERED, 0xd8, 0x13, EV_KEY, KEY_7, 1},
{KIND_FILTERED, 0xd9, 0x14, EV_KEY, KEY_8, 1},
{KIND_FILTERED, 0xda, 0x15, EV_KEY, KEY_9, 1},
{KIND_FILTERED, 0xdc, 0x17, EV_KEY, KEY_0, 1},
{KIND_FILTERED, 0xc5, 0x00, EV_KEY, KEY_A, 1},
{KIND_FILTERED, 0xc6, 0x01, EV_KEY, KEY_B, 1},
{KIND_FILTERED, 0xde, 0x19, EV_KEY, KEY_C, 1},
{KIND_FILTERED, 0xe0, 0x1b, EV_KEY, KEY_D, 1},
{KIND_FILTERED, 0xe6, 0x21, EV_KEY, KEY_E, 1},
{KIND_FILTERED, 0xe8, 0x23, EV_KEY, KEY_F, 1},
/* "special" keys */
{KIND_FILTERED, 0xdd, 0x18, EV_KEY, KEY_KPENTER, 1}, /* "check" */
{KIND_FILTERED, 0xdb, 0x16, EV_KEY, KEY_MENU, 1}, /* "menu" */
{KIND_FILTERED, 0xc7, 0x02, EV_KEY, KEY_POWER, 1}, /* Power */
{KIND_FILTERED, 0xc8, 0x03, EV_KEY, KEY_TV, 1}, /* TV */
{KIND_FILTERED, 0xc9, 0x04, EV_KEY, KEY_DVD, 1}, /* DVD */
{KIND_FILTERED, 0xca, 0x05, EV_KEY, KEY_WWW, 1}, /* WEB */
{KIND_FILTERED, 0xcb, 0x06, EV_KEY, KEY_BOOKMARKS, 1}, /* "book" */
{KIND_FILTERED, 0xcc, 0x07, EV_KEY, KEY_EDIT, 1}, /* "hand" */
{KIND_FILTERED, 0xe1, 0x1c, EV_KEY, KEY_COFFEE, 1}, /* "timer" */
{KIND_FILTERED, 0xe5, 0x20, EV_KEY, KEY_FRONT, 1}, /* "max" */
{KIND_FILTERED, 0xe2, 0x1d, EV_KEY, KEY_LEFT, 1}, /* left */
{KIND_FILTERED, 0xe4, 0x1f, EV_KEY, KEY_RIGHT, 1}, /* right */
{KIND_FILTERED, 0xe7, 0x22, EV_KEY, KEY_DOWN, 1}, /* down */
{KIND_FILTERED, 0xdf, 0x1a, EV_KEY, KEY_UP, 1}, /* up */
{KIND_FILTERED, 0xe3, 0x1e, EV_KEY, KEY_OK, 1}, /* "OK" */
{KIND_FILTERED, 0xce, 0x09, EV_KEY, KEY_VOLUMEDOWN, 1}, /* VOL + */
{KIND_FILTERED, 0xcd, 0x08, EV_KEY, KEY_VOLUMEUP, 1}, /* VOL - */
{KIND_FILTERED, 0xcf, 0x0a, EV_KEY, KEY_MUTE, 1}, /* MUTE */
{KIND_FILTERED, 0xd0, 0x0b, EV_KEY, KEY_CHANNELUP, 1}, /* CH + */
{KIND_FILTERED, 0xd1, 0x0c, EV_KEY, KEY_CHANNELDOWN, 1},/* CH - */
{KIND_FILTERED, 0xec, 0x27, EV_KEY, KEY_RECORD, 1}, /* ( o) red */
{KIND_FILTERED, 0xea, 0x25, EV_KEY, KEY_PLAY, 1}, /* ( >) */
{KIND_FILTERED, 0xe9, 0x24, EV_KEY, KEY_REWIND, 1}, /* (<<) */
{KIND_FILTERED, 0xeb, 0x26, EV_KEY, KEY_FORWARD, 1}, /* (>>) */
{KIND_FILTERED, 0xed, 0x28, EV_KEY, KEY_STOP, 1}, /* ([]) */
{KIND_FILTERED, 0xee, 0x29, EV_KEY, KEY_PAUSE, 1}, /* ('') */
{KIND_FILTERED, 0xf0, 0x2b, EV_KEY, KEY_PREVIOUS, 1}, /* (<-) */
{KIND_FILTERED, 0xef, 0x2a, EV_KEY, KEY_NEXT, 1}, /* (>+) */
{KIND_FILTERED, 0xf2, 0x2D, EV_KEY, KEY_INFO, 1}, /* PLAYING */
{KIND_FILTERED, 0xf3, 0x2E, EV_KEY, KEY_HOME, 1}, /* TOP */
{KIND_FILTERED, 0xf4, 0x2F, EV_KEY, KEY_END, 1}, /* END */
{KIND_FILTERED, 0xf5, 0x30, EV_KEY, KEY_SELECT, 1}, /* SELECT */
{KIND_END, 0x00, 0x00, EV_MAX + 1, 0, 0}
};
/* Local function prototypes */
static int ati_remote_open (struct input_dev *inputdev);
static void ati_remote_close (struct input_dev *inputdev);
static int ati_remote_sendpacket (struct ati_remote *ati_remote, u16 cmd, unsigned char *data);
static void ati_remote_irq_out (struct urb *urb);
static void ati_remote_irq_in (struct urb *urb);
static void ati_remote_input_report (struct urb *urb);
static int ati_remote_initialize (struct ati_remote *ati_remote);
static int ati_remote_probe (struct usb_interface *interface, const struct usb_device_id *id);
static void ati_remote_disconnect (struct usb_interface *interface);
/* usb specific object to register with the usb subsystem */
static struct usb_driver ati_remote_driver = {
.name = "ati_remote",
.probe = ati_remote_probe,
.disconnect = ati_remote_disconnect,
.id_table = ati_remote_table,
};
/*
* ati_remote_dump_input
*/
static void ati_remote_dump(struct device *dev, unsigned char *data,
unsigned int len)
{
if ((len == 1) && (data[0] != (unsigned char)0xff) && (data[0] != 0x00))
dev_warn(dev, "Weird byte 0x%02x\n", data[0]);
else if (len == 4)
dev_warn(dev, "Weird key %02x %02x %02x %02x\n",
data[0], data[1], data[2], data[3]);
else
dev_warn(dev, "Weird data, len=%d %02x %02x %02x %02x %02x %02x ...\n",
len, data[0], data[1], data[2], data[3], data[4], data[5]);
}
/*
* ati_remote_open
*/
static int ati_remote_open(struct input_dev *inputdev)
{
struct ati_remote *ati_remote = input_get_drvdata(inputdev);
/* On first open, submit the read urb which was set up previously. */
ati_remote->irq_urb->dev = ati_remote->udev;
if (usb_submit_urb(ati_remote->irq_urb, GFP_KERNEL)) {
dev_err(&ati_remote->interface->dev,
"%s: usb_submit_urb failed!\n", __func__);
return -EIO;
}
return 0;
}
/*
* ati_remote_close
*/
static void ati_remote_close(struct input_dev *inputdev)
{
struct ati_remote *ati_remote = input_get_drvdata(inputdev);
usb_kill_urb(ati_remote->irq_urb);
}
/*
* ati_remote_irq_out
*/
static void ati_remote_irq_out(struct urb *urb)
{
struct ati_remote *ati_remote = urb->context;
if (urb->status) {
dev_dbg(&ati_remote->interface->dev, "%s: status %d\n",
__func__, urb->status);
return;
}
ati_remote->send_flags |= SEND_FLAG_COMPLETE;
wmb();
wake_up(&ati_remote->wait);
}
/*
* ati_remote_sendpacket
*
* Used to send device initialization strings
*/
static int ati_remote_sendpacket(struct ati_remote *ati_remote, u16 cmd, unsigned char *data)
{
int retval = 0;
/* Set up out_urb */
memcpy(ati_remote->out_urb->transfer_buffer + 1, data, LO(cmd));
((char *) ati_remote->out_urb->transfer_buffer)[0] = HI(cmd);
ati_remote->out_urb->transfer_buffer_length = LO(cmd) + 1;
ati_remote->out_urb->dev = ati_remote->udev;
ati_remote->send_flags = SEND_FLAG_IN_PROGRESS;
retval = usb_submit_urb(ati_remote->out_urb, GFP_ATOMIC);
if (retval) {
dev_dbg(&ati_remote->interface->dev,
"sendpacket: usb_submit_urb failed: %d\n", retval);
return retval;
}
wait_event_timeout(ati_remote->wait,
((ati_remote->out_urb->status != -EINPROGRESS) ||
(ati_remote->send_flags & SEND_FLAG_COMPLETE)),
HZ);
usb_kill_urb(ati_remote->out_urb);
return retval;
}
/*
* ati_remote_event_lookup
*/
static int ati_remote_event_lookup(int rem, unsigned char d1, unsigned char d2)
{
int i;
for (i = 0; ati_remote_tbl[i].kind != KIND_END; i++) {
/*
* Decide if the table entry matches the remote input.
*/
if ((((ati_remote_tbl[i].data1 & 0x0f) == (d1 & 0x0f))) &&
((((ati_remote_tbl[i].data1 >> 4) -
(d1 >> 4) + rem) & 0x0f) == 0x0f) &&
(ati_remote_tbl[i].data2 == d2))
return i;
}
return -1;
}
/*
* ati_remote_compute_accel
*
* Implements acceleration curve for directional control pad
* If elapsed time since last event is > 1/4 second, user "stopped",
* so reset acceleration. Otherwise, user is probably holding the control
* pad down, so we increase acceleration, ramping up over two seconds to
* a maximum speed.
*/
static int ati_remote_compute_accel(struct ati_remote *ati_remote)
{
static const char accel[] = { 1, 2, 4, 6, 9, 13, 20 };
unsigned long now = jiffies;
int acc;
if (time_after(now, ati_remote->old_jiffies + msecs_to_jiffies(250))) {
acc = 1;
ati_remote->acc_jiffies = now;
}
else if (time_before(now, ati_remote->acc_jiffies + msecs_to_jiffies(125)))
acc = accel[0];
else if (time_before(now, ati_remote->acc_jiffies + msecs_to_jiffies(250)))
acc = accel[1];
else if (time_before(now, ati_remote->acc_jiffies + msecs_to_jiffies(500)))
acc = accel[2];
else if (time_before(now, ati_remote->acc_jiffies + msecs_to_jiffies(1000)))
acc = accel[3];
else if (time_before(now, ati_remote->acc_jiffies + msecs_to_jiffies(1500)))
acc = accel[4];
else if (time_before(now, ati_remote->acc_jiffies + msecs_to_jiffies(2000)))
acc = accel[5];
else
acc = accel[6];
return acc;
}
/*
* ati_remote_report_input
*/
static void ati_remote_input_report(struct urb *urb)
{
struct ati_remote *ati_remote = urb->context;
unsigned char *data= ati_remote->inbuf;
struct input_dev *dev = ati_remote->idev;
int index, acc;
int remote_num;
/* Deal with strange looking inputs */
if ( (urb->actual_length != 4) || (data[0] != 0x14) ||
((data[3] & 0x0f) != 0x00) ) {
ati_remote_dump(&urb->dev->dev, data, urb->actual_length);
return;
}
/* Mask unwanted remote channels. */
/* note: remote_num is 0-based, channel 1 on remote == 0 here */
remote_num = (data[3] >> 4) & 0x0f;
if (channel_mask & (1 << (remote_num + 1))) {
dbginfo(&ati_remote->interface->dev,
"Masked input from channel 0x%02x: data %02x,%02x, mask= 0x%02lx\n",
remote_num, data[1], data[2], channel_mask);
return;
}
/* Look up event code index in translation table */
index = ati_remote_event_lookup(remote_num, data[1], data[2]);
if (index < 0) {
dev_warn(&ati_remote->interface->dev,
"Unknown input from channel 0x%02x: data %02x,%02x\n",
remote_num, data[1], data[2]);
return;
}
dbginfo(&ati_remote->interface->dev,
"channel 0x%02x; data %02x,%02x; index %d; keycode %d\n",
remote_num, data[1], data[2], index, ati_remote_tbl[index].code);
if (ati_remote_tbl[index].kind == KIND_LITERAL) {
input_event(dev, ati_remote_tbl[index].type,
ati_remote_tbl[index].code,
ati_remote_tbl[index].value);
input_sync(dev);
ati_remote->old_jiffies = jiffies;
return;
}
if (ati_remote_tbl[index].kind == KIND_FILTERED) {
unsigned long now = jiffies;
/* Filter duplicate events which happen "too close" together. */
if (ati_remote->old_data[0] == data[1] &&
ati_remote->old_data[1] == data[2] &&
time_before(now, ati_remote->old_jiffies +
msecs_to_jiffies(repeat_filter))) {
ati_remote->repeat_count++;
} else {
ati_remote->repeat_count = 0;
ati_remote->first_jiffies = now;
}
ati_remote->old_data[0] = data[1];
ati_remote->old_data[1] = data[2];
ati_remote->old_jiffies = now;
/* Ensure we skip at least the 4 first duplicate events (generated
* by a single keypress), and continue skipping until repeat_delay
* msecs have passed
*/
if (ati_remote->repeat_count > 0 &&
(ati_remote->repeat_count < 5 ||
time_before(now, ati_remote->first_jiffies +
msecs_to_jiffies(repeat_delay))))
return;
input_event(dev, ati_remote_tbl[index].type,
ati_remote_tbl[index].code, 1);
input_sync(dev);
input_event(dev, ati_remote_tbl[index].type,
ati_remote_tbl[index].code, 0);
input_sync(dev);
} else {
/*
* Other event kinds are from the directional control pad, and have an
* acceleration factor applied to them. Without this acceleration, the
* control pad is mostly unusable.
*/
acc = ati_remote_compute_accel(ati_remote);
switch (ati_remote_tbl[index].kind) {
case KIND_ACCEL:
input_event(dev, ati_remote_tbl[index].type,
ati_remote_tbl[index].code,
ati_remote_tbl[index].value * acc);
break;
case KIND_LU:
input_report_rel(dev, REL_X, -acc);
input_report_rel(dev, REL_Y, -acc);
break;
case KIND_RU:
input_report_rel(dev, REL_X, acc);
input_report_rel(dev, REL_Y, -acc);
break;
case KIND_LD:
input_report_rel(dev, REL_X, -acc);
input_report_rel(dev, REL_Y, acc);
break;
case KIND_RD:
input_report_rel(dev, REL_X, acc);
input_report_rel(dev, REL_Y, acc);
break;
default:
dev_dbg(&ati_remote->interface->dev, "ati_remote kind=%d\n",
ati_remote_tbl[index].kind);
}
input_sync(dev);
ati_remote->old_jiffies = jiffies;
ati_remote->old_data[0] = data[1];
ati_remote->old_data[1] = data[2];
}
}
/*
* ati_remote_irq_in
*/
static void ati_remote_irq_in(struct urb *urb)
{
struct ati_remote *ati_remote = urb->context;
int retval;
switch (urb->status) {
case 0: /* success */
ati_remote_input_report(urb);
break;
case -ECONNRESET: /* unlink */
case -ENOENT:
case -ESHUTDOWN:
dev_dbg(&ati_remote->interface->dev, "%s: urb error status, unlink? \n",
__func__);
return;
default: /* error */
dev_dbg(&ati_remote->interface->dev, "%s: Nonzero urb status %d\n",
__func__, urb->status);
}
retval = usb_submit_urb(urb, GFP_ATOMIC);
if (retval)
dev_err(&ati_remote->interface->dev, "%s: usb_submit_urb()=%d\n",
__func__, retval);
}
/*
* ati_remote_alloc_buffers
*/
static int ati_remote_alloc_buffers(struct usb_device *udev,
struct ati_remote *ati_remote)
{
ati_remote->inbuf = usb_buffer_alloc(udev, DATA_BUFSIZE, GFP_ATOMIC,
&ati_remote->inbuf_dma);
if (!ati_remote->inbuf)
return -1;
ati_remote->outbuf = usb_buffer_alloc(udev, DATA_BUFSIZE, GFP_ATOMIC,
&ati_remote->outbuf_dma);
if (!ati_remote->outbuf)
return -1;
ati_remote->irq_urb = usb_alloc_urb(0, GFP_KERNEL);
if (!ati_remote->irq_urb)
return -1;
ati_remote->out_urb = usb_alloc_urb(0, GFP_KERNEL);
if (!ati_remote->out_urb)
return -1;
return 0;
}
/*
* ati_remote_free_buffers
*/
static void ati_remote_free_buffers(struct ati_remote *ati_remote)
{
usb_free_urb(ati_remote->irq_urb);
usb_free_urb(ati_remote->out_urb);
usb_buffer_free(ati_remote->udev, DATA_BUFSIZE,
ati_remote->inbuf, ati_remote->inbuf_dma);
usb_buffer_free(ati_remote->udev, DATA_BUFSIZE,
ati_remote->outbuf, ati_remote->outbuf_dma);
}
static void ati_remote_input_init(struct ati_remote *ati_remote)
{
struct input_dev *idev = ati_remote->idev;
int i;
idev->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_REL);
idev->keybit[BIT_WORD(BTN_MOUSE)] = BIT_MASK(BTN_LEFT) |
BIT_MASK(BTN_RIGHT) | BIT_MASK(BTN_SIDE) | BIT_MASK(BTN_EXTRA);
idev->relbit[0] = BIT_MASK(REL_X) | BIT_MASK(REL_Y);
for (i = 0; ati_remote_tbl[i].kind != KIND_END; i++)
if (ati_remote_tbl[i].type == EV_KEY)
set_bit(ati_remote_tbl[i].code, idev->keybit);
input_set_drvdata(idev, ati_remote);
idev->open = ati_remote_open;
idev->close = ati_remote_close;
idev->name = ati_remote->name;
idev->phys = ati_remote->phys;
usb_to_input_id(ati_remote->udev, &idev->id);
idev->dev.parent = &ati_remote->udev->dev;
}
static int ati_remote_initialize(struct ati_remote *ati_remote)
{
struct usb_device *udev = ati_remote->udev;
int pipe, maxp;
init_waitqueue_head(&ati_remote->wait);
/* Set up irq_urb */
pipe = usb_rcvintpipe(udev, ati_remote->endpoint_in->bEndpointAddress);
maxp = usb_maxpacket(udev, pipe, usb_pipeout(pipe));
maxp = (maxp > DATA_BUFSIZE) ? DATA_BUFSIZE : maxp;
usb_fill_int_urb(ati_remote->irq_urb, udev, pipe, ati_remote->inbuf,
maxp, ati_remote_irq_in, ati_remote,
ati_remote->endpoint_in->bInterval);
ati_remote->irq_urb->transfer_dma = ati_remote->inbuf_dma;
ati_remote->irq_urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;
/* Set up out_urb */
pipe = usb_sndintpipe(udev, ati_remote->endpoint_out->bEndpointAddress);
maxp = usb_maxpacket(udev, pipe, usb_pipeout(pipe));
maxp = (maxp > DATA_BUFSIZE) ? DATA_BUFSIZE : maxp;
usb_fill_int_urb(ati_remote->out_urb, udev, pipe, ati_remote->outbuf,
maxp, ati_remote_irq_out, ati_remote,
ati_remote->endpoint_out->bInterval);
ati_remote->out_urb->transfer_dma = ati_remote->outbuf_dma;
ati_remote->out_urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;
/* send initialization strings */
if ((ati_remote_sendpacket(ati_remote, 0x8004, init1)) ||
(ati_remote_sendpacket(ati_remote, 0x8007, init2))) {
dev_err(&ati_remote->interface->dev,
"Initializing ati_remote hardware failed.\n");
return -EIO;
}
return 0;
}
/*
* ati_remote_probe
*/
static int ati_remote_probe(struct usb_interface *interface, const struct usb_device_id *id)
{
struct usb_device *udev = interface_to_usbdev(interface);
struct usb_host_interface *iface_host = interface->cur_altsetting;
struct usb_endpoint_descriptor *endpoint_in, *endpoint_out;
struct ati_remote *ati_remote;
struct input_dev *input_dev;
int err = -ENOMEM;
if (iface_host->desc.bNumEndpoints != 2) {
err("%s: Unexpected desc.bNumEndpoints\n", __func__);
return -ENODEV;
}
endpoint_in = &iface_host->endpoint[0].desc;
endpoint_out = &iface_host->endpoint[1].desc;
if (!usb_endpoint_is_int_in(endpoint_in)) {
err("%s: Unexpected endpoint_in\n", __func__);
return -ENODEV;
}
if (le16_to_cpu(endpoint_in->wMaxPacketSize) == 0) {
err("%s: endpoint_in message size==0? \n", __func__);
return -ENODEV;
}
ati_remote = kzalloc(sizeof (struct ati_remote), GFP_KERNEL);
input_dev = input_allocate_device();
if (!ati_remote || !input_dev)
goto fail1;
/* Allocate URB buffers, URBs */
if (ati_remote_alloc_buffers(udev, ati_remote))
goto fail2;
ati_remote->endpoint_in = endpoint_in;
ati_remote->endpoint_out = endpoint_out;
ati_remote->udev = udev;
ati_remote->idev = input_dev;
ati_remote->interface = interface;
usb_make_path(udev, ati_remote->phys, sizeof(ati_remote->phys));
strlcpy(ati_remote->phys, "/input0", sizeof(ati_remote->phys));
if (udev->manufacturer)
strlcpy(ati_remote->name, udev->manufacturer, sizeof(ati_remote->name));
if (udev->product)
snprintf(ati_remote->name, sizeof(ati_remote->name),
"%s %s", ati_remote->name, udev->product);
if (!strlen(ati_remote->name))
snprintf(ati_remote->name, sizeof(ati_remote->name),
DRIVER_DESC "(%04x,%04x)",
le16_to_cpu(ati_remote->udev->descriptor.idVendor),
le16_to_cpu(ati_remote->udev->descriptor.idProduct));
ati_remote_input_init(ati_remote);
/* Device Hardware Initialization - fills in ati_remote->idev from udev. */
err = ati_remote_initialize(ati_remote);
if (err)
goto fail3;
/* Set up and register input device */
err = input_register_device(ati_remote->idev);
if (err)
goto fail3;
usb_set_intfdata(interface, ati_remote);
return 0;
fail3: usb_kill_urb(ati_remote->irq_urb);
usb_kill_urb(ati_remote->out_urb);
fail2: ati_remote_free_buffers(ati_remote);
fail1: input_free_device(input_dev);
kfree(ati_remote);
return err;
}
/*
* ati_remote_disconnect
*/
static void ati_remote_disconnect(struct usb_interface *interface)
{
struct ati_remote *ati_remote;
ati_remote = usb_get_intfdata(interface);
usb_set_intfdata(interface, NULL);
if (!ati_remote) {
dev_warn(&interface->dev, "%s - null device?\n", __func__);
return;
}
usb_kill_urb(ati_remote->irq_urb);
usb_kill_urb(ati_remote->out_urb);
input_unregister_device(ati_remote->idev);
ati_remote_free_buffers(ati_remote);
kfree(ati_remote);
}
/*
* ati_remote_init
*/
static int __init ati_remote_init(void)
{
int result;
result = usb_register(&ati_remote_driver);
if (result)
printk(KERN_ERR KBUILD_MODNAME
": usb_register error #%d\n", result);
else
printk(KERN_INFO KBUILD_MODNAME ": " DRIVER_VERSION ":"
DRIVER_DESC "\n");
return result;
}
/*
* ati_remote_exit
*/
static void __exit ati_remote_exit(void)
{
usb_deregister(&ati_remote_driver);
}
/*
* module specification
*/
module_init(ati_remote_init);
module_exit(ati_remote_exit);
MODULE_AUTHOR(DRIVER_AUTHOR);
MODULE_DESCRIPTION(DRIVER_DESC);
MODULE_LICENSE("GPL");

View File

@@ -0,0 +1,982 @@
/*
* ati_remote2 - ATI/Philips USB RF remote driver
*
* Copyright (C) 2005-2008 Ville Syrjala <syrjala@sci.fi>
* Copyright (C) 2007-2008 Peter Stokes <linux@dadeos.co.uk>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2
* as published by the Free Software Foundation.
*/
#include <linux/usb/input.h>
#define DRIVER_DESC "ATI/Philips USB RF remote driver"
#define DRIVER_VERSION "0.3"
MODULE_DESCRIPTION(DRIVER_DESC);
MODULE_VERSION(DRIVER_VERSION);
MODULE_AUTHOR("Ville Syrjala <syrjala@sci.fi>");
MODULE_LICENSE("GPL");
/*
* ATI Remote Wonder II Channel Configuration
*
* The remote control can by assigned one of sixteen "channels" in order to facilitate
* the use of multiple remote controls within range of each other.
* A remote's "channel" may be altered by pressing and holding the "PC" button for
* approximately 3 seconds, after which the button will slowly flash the count of the
* currently configured "channel", using the numeric keypad enter a number between 1 and
* 16 and then press the "PC" button again, the button will slowly flash the count of the
* newly configured "channel".
*/
enum {
ATI_REMOTE2_MAX_CHANNEL_MASK = 0xFFFF,
ATI_REMOTE2_MAX_MODE_MASK = 0x1F,
};
static int ati_remote2_set_mask(const char *val,
struct kernel_param *kp, unsigned int max)
{
unsigned long mask;
int ret;
if (!val)
return -EINVAL;
ret = strict_strtoul(val, 0, &mask);
if (ret)
return ret;
if (mask & ~max)
return -EINVAL;
*(unsigned int *)kp->arg = mask;
return 0;
}
static int ati_remote2_set_channel_mask(const char *val,
struct kernel_param *kp)
{
pr_debug("%s()\n", __func__);
return ati_remote2_set_mask(val, kp, ATI_REMOTE2_MAX_CHANNEL_MASK);
}
static int ati_remote2_get_channel_mask(char *buffer, struct kernel_param *kp)
{
pr_debug("%s()\n", __func__);
return sprintf(buffer, "0x%04x", *(unsigned int *)kp->arg);
}
static int ati_remote2_set_mode_mask(const char *val, struct kernel_param *kp)
{
pr_debug("%s()\n", __func__);
return ati_remote2_set_mask(val, kp, ATI_REMOTE2_MAX_MODE_MASK);
}
static int ati_remote2_get_mode_mask(char *buffer, struct kernel_param *kp)
{
pr_debug("%s()\n", __func__);
return sprintf(buffer, "0x%02x", *(unsigned int *)kp->arg);
}
static unsigned int channel_mask = ATI_REMOTE2_MAX_CHANNEL_MASK;
#define param_check_channel_mask(name, p) __param_check(name, p, unsigned int)
#define param_set_channel_mask ati_remote2_set_channel_mask
#define param_get_channel_mask ati_remote2_get_channel_mask
module_param(channel_mask, channel_mask, 0644);
MODULE_PARM_DESC(channel_mask, "Bitmask of channels to accept <15:Channel16>...<1:Channel2><0:Channel1>");
static unsigned int mode_mask = ATI_REMOTE2_MAX_MODE_MASK;
#define param_check_mode_mask(name, p) __param_check(name, p, unsigned int)
#define param_set_mode_mask ati_remote2_set_mode_mask
#define param_get_mode_mask ati_remote2_get_mode_mask
module_param(mode_mask, mode_mask, 0644);
MODULE_PARM_DESC(mode_mask, "Bitmask of modes to accept <4:PC><3:AUX4><2:AUX3><1:AUX2><0:AUX1>");
static struct usb_device_id ati_remote2_id_table[] = {
{ USB_DEVICE(0x0471, 0x0602) }, /* ATI Remote Wonder II */
{ }
};
MODULE_DEVICE_TABLE(usb, ati_remote2_id_table);
static DEFINE_MUTEX(ati_remote2_mutex);
enum {
ATI_REMOTE2_OPENED = 0x1,
ATI_REMOTE2_SUSPENDED = 0x2,
};
enum {
ATI_REMOTE2_AUX1,
ATI_REMOTE2_AUX2,
ATI_REMOTE2_AUX3,
ATI_REMOTE2_AUX4,
ATI_REMOTE2_PC,
ATI_REMOTE2_MODES,
};
static const struct {
u8 hw_code;
u16 keycode;
} ati_remote2_key_table[] = {
{ 0x00, KEY_0 },
{ 0x01, KEY_1 },
{ 0x02, KEY_2 },
{ 0x03, KEY_3 },
{ 0x04, KEY_4 },
{ 0x05, KEY_5 },
{ 0x06, KEY_6 },
{ 0x07, KEY_7 },
{ 0x08, KEY_8 },
{ 0x09, KEY_9 },
{ 0x0c, KEY_POWER },
{ 0x0d, KEY_MUTE },
{ 0x10, KEY_VOLUMEUP },
{ 0x11, KEY_VOLUMEDOWN },
{ 0x20, KEY_CHANNELUP },
{ 0x21, KEY_CHANNELDOWN },
{ 0x28, KEY_FORWARD },
{ 0x29, KEY_REWIND },
{ 0x2c, KEY_PLAY },
{ 0x30, KEY_PAUSE },
{ 0x31, KEY_STOP },
{ 0x37, KEY_RECORD },
{ 0x38, KEY_DVD },
{ 0x39, KEY_TV },
{ 0x3f, KEY_PROG1 }, /* AUX1-AUX4 and PC */
{ 0x54, KEY_MENU },
{ 0x58, KEY_UP },
{ 0x59, KEY_DOWN },
{ 0x5a, KEY_LEFT },
{ 0x5b, KEY_RIGHT },
{ 0x5c, KEY_OK },
{ 0x78, KEY_A },
{ 0x79, KEY_B },
{ 0x7a, KEY_C },
{ 0x7b, KEY_D },
{ 0x7c, KEY_E },
{ 0x7d, KEY_F },
{ 0x82, KEY_ENTER },
{ 0x8e, KEY_VENDOR },
{ 0x96, KEY_COFFEE },
{ 0xa9, BTN_LEFT },
{ 0xaa, BTN_RIGHT },
{ 0xbe, KEY_QUESTION },
{ 0xd0, KEY_EDIT },
{ 0xd5, KEY_FRONT },
{ 0xf9, KEY_INFO },
};
struct ati_remote2 {
struct input_dev *idev;
struct usb_device *udev;
struct usb_interface *intf[2];
struct usb_endpoint_descriptor *ep[2];
struct urb *urb[2];
void *buf[2];
dma_addr_t buf_dma[2];
unsigned long jiffies;
int mode;
char name[64];
char phys[64];
/* Each mode (AUX1-AUX4 and PC) can have an independent keymap. */
u16 keycode[ATI_REMOTE2_MODES][ARRAY_SIZE(ati_remote2_key_table)];
unsigned int flags;
unsigned int channel_mask;
unsigned int mode_mask;
};
static int ati_remote2_probe(struct usb_interface *interface, const struct usb_device_id *id);
static void ati_remote2_disconnect(struct usb_interface *interface);
static int ati_remote2_suspend(struct usb_interface *interface, pm_message_t message);
static int ati_remote2_resume(struct usb_interface *interface);
static int ati_remote2_reset_resume(struct usb_interface *interface);
static int ati_remote2_pre_reset(struct usb_interface *interface);
static int ati_remote2_post_reset(struct usb_interface *interface);
static struct usb_driver ati_remote2_driver = {
.name = "ati_remote2",
.probe = ati_remote2_probe,
.disconnect = ati_remote2_disconnect,
.id_table = ati_remote2_id_table,
.suspend = ati_remote2_suspend,
.resume = ati_remote2_resume,
.reset_resume = ati_remote2_reset_resume,
.pre_reset = ati_remote2_pre_reset,
.post_reset = ati_remote2_post_reset,
.supports_autosuspend = 1,
};
static int ati_remote2_submit_urbs(struct ati_remote2 *ar2)
{
int r;
r = usb_submit_urb(ar2->urb[0], GFP_KERNEL);
if (r) {
dev_err(&ar2->intf[0]->dev,
"%s(): usb_submit_urb() = %d\n", __func__, r);
return r;
}
r = usb_submit_urb(ar2->urb[1], GFP_KERNEL);
if (r) {
usb_kill_urb(ar2->urb[0]);
dev_err(&ar2->intf[1]->dev,
"%s(): usb_submit_urb() = %d\n", __func__, r);
return r;
}
return 0;
}
static void ati_remote2_kill_urbs(struct ati_remote2 *ar2)
{
usb_kill_urb(ar2->urb[1]);
usb_kill_urb(ar2->urb[0]);
}
static int ati_remote2_open(struct input_dev *idev)
{
struct ati_remote2 *ar2 = input_get_drvdata(idev);
int r;
dev_dbg(&ar2->intf[0]->dev, "%s()\n", __func__);
r = usb_autopm_get_interface(ar2->intf[0]);
if (r) {
dev_err(&ar2->intf[0]->dev,
"%s(): usb_autopm_get_interface() = %d\n", __func__, r);
goto fail1;
}
mutex_lock(&ati_remote2_mutex);
if (!(ar2->flags & ATI_REMOTE2_SUSPENDED)) {
r = ati_remote2_submit_urbs(ar2);
if (r)
goto fail2;
}
ar2->flags |= ATI_REMOTE2_OPENED;
mutex_unlock(&ati_remote2_mutex);
usb_autopm_put_interface(ar2->intf[0]);
return 0;
fail2:
mutex_unlock(&ati_remote2_mutex);
usb_autopm_put_interface(ar2->intf[0]);
fail1:
return r;
}
static void ati_remote2_close(struct input_dev *idev)
{
struct ati_remote2 *ar2 = input_get_drvdata(idev);
dev_dbg(&ar2->intf[0]->dev, "%s()\n", __func__);
mutex_lock(&ati_remote2_mutex);
if (!(ar2->flags & ATI_REMOTE2_SUSPENDED))
ati_remote2_kill_urbs(ar2);
ar2->flags &= ~ATI_REMOTE2_OPENED;
mutex_unlock(&ati_remote2_mutex);
}
static void ati_remote2_input_mouse(struct ati_remote2 *ar2)
{
struct input_dev *idev = ar2->idev;
u8 *data = ar2->buf[0];
int channel, mode;
channel = data[0] >> 4;
if (!((1 << channel) & ar2->channel_mask))
return;
mode = data[0] & 0x0F;
if (mode > ATI_REMOTE2_PC) {
dev_err(&ar2->intf[0]->dev,
"Unknown mode byte (%02x %02x %02x %02x)\n",
data[3], data[2], data[1], data[0]);
return;
}
if (!((1 << mode) & ar2->mode_mask))
return;
input_event(idev, EV_REL, REL_X, (s8) data[1]);
input_event(idev, EV_REL, REL_Y, (s8) data[2]);
input_sync(idev);
}
static int ati_remote2_lookup(unsigned int hw_code)
{
int i;
for (i = 0; i < ARRAY_SIZE(ati_remote2_key_table); i++)
if (ati_remote2_key_table[i].hw_code == hw_code)
return i;
return -1;
}
static void ati_remote2_input_key(struct ati_remote2 *ar2)
{
struct input_dev *idev = ar2->idev;
u8 *data = ar2->buf[1];
int channel, mode, hw_code, index;
channel = data[0] >> 4;
if (!((1 << channel) & ar2->channel_mask))
return;
mode = data[0] & 0x0F;
if (mode > ATI_REMOTE2_PC) {
dev_err(&ar2->intf[1]->dev,
"Unknown mode byte (%02x %02x %02x %02x)\n",
data[3], data[2], data[1], data[0]);
return;
}
hw_code = data[2];
if (hw_code == 0x3f) {
/*
* For some incomprehensible reason the mouse pad generates
* events which look identical to the events from the last
* pressed mode key. Naturally we don't want to generate key
* events for the mouse pad so we filter out any subsequent
* events from the same mode key.
*/
if (ar2->mode == mode)
return;
if (data[1] == 0)
ar2->mode = mode;
}
if (!((1 << mode) & ar2->mode_mask))
return;
index = ati_remote2_lookup(hw_code);
if (index < 0) {
dev_err(&ar2->intf[1]->dev,
"Unknown code byte (%02x %02x %02x %02x)\n",
data[3], data[2], data[1], data[0]);
return;
}
switch (data[1]) {
case 0: /* release */
break;
case 1: /* press */
ar2->jiffies = jiffies + msecs_to_jiffies(idev->rep[REP_DELAY]);
break;
case 2: /* repeat */
/* No repeat for mouse buttons. */
if (ar2->keycode[mode][index] == BTN_LEFT ||
ar2->keycode[mode][index] == BTN_RIGHT)
return;
if (!time_after_eq(jiffies, ar2->jiffies))
return;
ar2->jiffies = jiffies + msecs_to_jiffies(idev->rep[REP_PERIOD]);
break;
default:
dev_err(&ar2->intf[1]->dev,
"Unknown state byte (%02x %02x %02x %02x)\n",
data[3], data[2], data[1], data[0]);
return;
}
input_event(idev, EV_KEY, ar2->keycode[mode][index], data[1]);
input_sync(idev);
}
static void ati_remote2_complete_mouse(struct urb *urb)
{
struct ati_remote2 *ar2 = urb->context;
int r;
switch (urb->status) {
case 0:
usb_mark_last_busy(ar2->udev);
ati_remote2_input_mouse(ar2);
break;
case -ENOENT:
case -EILSEQ:
case -ECONNRESET:
case -ESHUTDOWN:
dev_dbg(&ar2->intf[0]->dev,
"%s(): urb status = %d\n", __func__, urb->status);
return;
default:
usb_mark_last_busy(ar2->udev);
dev_err(&ar2->intf[0]->dev,
"%s(): urb status = %d\n", __func__, urb->status);
}
r = usb_submit_urb(urb, GFP_ATOMIC);
if (r)
dev_err(&ar2->intf[0]->dev,
"%s(): usb_submit_urb() = %d\n", __func__, r);
}
static void ati_remote2_complete_key(struct urb *urb)
{
struct ati_remote2 *ar2 = urb->context;
int r;
switch (urb->status) {
case 0:
usb_mark_last_busy(ar2->udev);
ati_remote2_input_key(ar2);
break;
case -ENOENT:
case -EILSEQ:
case -ECONNRESET:
case -ESHUTDOWN:
dev_dbg(&ar2->intf[1]->dev,
"%s(): urb status = %d\n", __func__, urb->status);
return;
default:
usb_mark_last_busy(ar2->udev);
dev_err(&ar2->intf[1]->dev,
"%s(): urb status = %d\n", __func__, urb->status);
}
r = usb_submit_urb(urb, GFP_ATOMIC);
if (r)
dev_err(&ar2->intf[1]->dev,
"%s(): usb_submit_urb() = %d\n", __func__, r);
}
static int ati_remote2_getkeycode(struct input_dev *idev,
int scancode, int *keycode)
{
struct ati_remote2 *ar2 = input_get_drvdata(idev);
int index, mode;
mode = scancode >> 8;
if (mode > ATI_REMOTE2_PC || !((1 << mode) & ar2->mode_mask))
return -EINVAL;
index = ati_remote2_lookup(scancode & 0xFF);
if (index < 0)
return -EINVAL;
*keycode = ar2->keycode[mode][index];
return 0;
}
static int ati_remote2_setkeycode(struct input_dev *idev, int scancode, int keycode)
{
struct ati_remote2 *ar2 = input_get_drvdata(idev);
int index, mode, old_keycode;
mode = scancode >> 8;
if (mode > ATI_REMOTE2_PC || !((1 << mode) & ar2->mode_mask))
return -EINVAL;
index = ati_remote2_lookup(scancode & 0xFF);
if (index < 0)
return -EINVAL;
if (keycode < KEY_RESERVED || keycode > KEY_MAX)
return -EINVAL;
old_keycode = ar2->keycode[mode][index];
ar2->keycode[mode][index] = keycode;
__set_bit(keycode, idev->keybit);
for (mode = 0; mode < ATI_REMOTE2_MODES; mode++) {
for (index = 0; index < ARRAY_SIZE(ati_remote2_key_table); index++) {
if (ar2->keycode[mode][index] == old_keycode)
return 0;
}
}
__clear_bit(old_keycode, idev->keybit);
return 0;
}
static int ati_remote2_input_init(struct ati_remote2 *ar2)
{
struct input_dev *idev;
int index, mode, retval;
idev = input_allocate_device();
if (!idev)
return -ENOMEM;
ar2->idev = idev;
input_set_drvdata(idev, ar2);
idev->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_REP) | BIT_MASK(EV_REL);
idev->keybit[BIT_WORD(BTN_MOUSE)] = BIT_MASK(BTN_LEFT) |
BIT_MASK(BTN_RIGHT);
idev->relbit[0] = BIT_MASK(REL_X) | BIT_MASK(REL_Y);
for (mode = 0; mode < ATI_REMOTE2_MODES; mode++) {
for (index = 0; index < ARRAY_SIZE(ati_remote2_key_table); index++) {
ar2->keycode[mode][index] = ati_remote2_key_table[index].keycode;
__set_bit(ar2->keycode[mode][index], idev->keybit);
}
}
/* AUX1-AUX4 and PC generate the same scancode. */
index = ati_remote2_lookup(0x3f);
ar2->keycode[ATI_REMOTE2_AUX1][index] = KEY_PROG1;
ar2->keycode[ATI_REMOTE2_AUX2][index] = KEY_PROG2;
ar2->keycode[ATI_REMOTE2_AUX3][index] = KEY_PROG3;
ar2->keycode[ATI_REMOTE2_AUX4][index] = KEY_PROG4;
ar2->keycode[ATI_REMOTE2_PC][index] = KEY_PC;
__set_bit(KEY_PROG1, idev->keybit);
__set_bit(KEY_PROG2, idev->keybit);
__set_bit(KEY_PROG3, idev->keybit);
__set_bit(KEY_PROG4, idev->keybit);
__set_bit(KEY_PC, idev->keybit);
idev->rep[REP_DELAY] = 250;
idev->rep[REP_PERIOD] = 33;
idev->open = ati_remote2_open;
idev->close = ati_remote2_close;
idev->getkeycode = ati_remote2_getkeycode;
idev->setkeycode = ati_remote2_setkeycode;
idev->name = ar2->name;
idev->phys = ar2->phys;
usb_to_input_id(ar2->udev, &idev->id);
idev->dev.parent = &ar2->udev->dev;
retval = input_register_device(idev);
if (retval)
input_free_device(idev);
return retval;
}
static int ati_remote2_urb_init(struct ati_remote2 *ar2)
{
struct usb_device *udev = ar2->udev;
int i, pipe, maxp;
for (i = 0; i < 2; i++) {
ar2->buf[i] = usb_buffer_alloc(udev, 4, GFP_KERNEL, &ar2->buf_dma[i]);
if (!ar2->buf[i])
return -ENOMEM;
ar2->urb[i] = usb_alloc_urb(0, GFP_KERNEL);
if (!ar2->urb[i])
return -ENOMEM;
pipe = usb_rcvintpipe(udev, ar2->ep[i]->bEndpointAddress);
maxp = usb_maxpacket(udev, pipe, usb_pipeout(pipe));
maxp = maxp > 4 ? 4 : maxp;
usb_fill_int_urb(ar2->urb[i], udev, pipe, ar2->buf[i], maxp,
i ? ati_remote2_complete_key : ati_remote2_complete_mouse,
ar2, ar2->ep[i]->bInterval);
ar2->urb[i]->transfer_dma = ar2->buf_dma[i];
ar2->urb[i]->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;
}
return 0;
}
static void ati_remote2_urb_cleanup(struct ati_remote2 *ar2)
{
int i;
for (i = 0; i < 2; i++) {
usb_free_urb(ar2->urb[i]);
usb_buffer_free(ar2->udev, 4, ar2->buf[i], ar2->buf_dma[i]);
}
}
static int ati_remote2_setup(struct ati_remote2 *ar2, unsigned int ch_mask)
{
int r, i, channel;
/*
* Configure receiver to only accept input from remote "channel"
* channel == 0 -> Accept input from any remote channel
* channel == 1 -> Only accept input from remote channel 1
* channel == 2 -> Only accept input from remote channel 2
* ...
* channel == 16 -> Only accept input from remote channel 16
*/
channel = 0;
for (i = 0; i < 16; i++) {
if ((1 << i) & ch_mask) {
if (!(~(1 << i) & ch_mask))
channel = i + 1;
break;
}
}
r = usb_control_msg(ar2->udev, usb_sndctrlpipe(ar2->udev, 0),
0x20,
USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_INTERFACE,
channel, 0x0, NULL, 0, USB_CTRL_SET_TIMEOUT);
if (r) {
dev_err(&ar2->udev->dev, "%s - failed to set channel due to error: %d\n",
__func__, r);
return r;
}
return 0;
}
static ssize_t ati_remote2_show_channel_mask(struct device *dev,
struct device_attribute *attr,
char *buf)
{
struct usb_device *udev = to_usb_device(dev);
struct usb_interface *intf = usb_ifnum_to_if(udev, 0);
struct ati_remote2 *ar2 = usb_get_intfdata(intf);
return sprintf(buf, "0x%04x\n", ar2->channel_mask);
}
static ssize_t ati_remote2_store_channel_mask(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t count)
{
struct usb_device *udev = to_usb_device(dev);
struct usb_interface *intf = usb_ifnum_to_if(udev, 0);
struct ati_remote2 *ar2 = usb_get_intfdata(intf);
unsigned long mask;
int r;
if (strict_strtoul(buf, 0, &mask))
return -EINVAL;
if (mask & ~ATI_REMOTE2_MAX_CHANNEL_MASK)
return -EINVAL;
r = usb_autopm_get_interface(ar2->intf[0]);
if (r) {
dev_err(&ar2->intf[0]->dev,
"%s(): usb_autopm_get_interface() = %d\n", __func__, r);
return r;
}
mutex_lock(&ati_remote2_mutex);
if (mask != ar2->channel_mask && !ati_remote2_setup(ar2, mask))
ar2->channel_mask = mask;
mutex_unlock(&ati_remote2_mutex);
usb_autopm_put_interface(ar2->intf[0]);
return count;
}
static ssize_t ati_remote2_show_mode_mask(struct device *dev,
struct device_attribute *attr,
char *buf)
{
struct usb_device *udev = to_usb_device(dev);
struct usb_interface *intf = usb_ifnum_to_if(udev, 0);
struct ati_remote2 *ar2 = usb_get_intfdata(intf);
return sprintf(buf, "0x%02x\n", ar2->mode_mask);
}
static ssize_t ati_remote2_store_mode_mask(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t count)
{
struct usb_device *udev = to_usb_device(dev);
struct usb_interface *intf = usb_ifnum_to_if(udev, 0);
struct ati_remote2 *ar2 = usb_get_intfdata(intf);
unsigned long mask;
if (strict_strtoul(buf, 0, &mask))
return -EINVAL;
if (mask & ~ATI_REMOTE2_MAX_MODE_MASK)
return -EINVAL;
ar2->mode_mask = mask;
return count;
}
static DEVICE_ATTR(channel_mask, 0644, ati_remote2_show_channel_mask,
ati_remote2_store_channel_mask);
static DEVICE_ATTR(mode_mask, 0644, ati_remote2_show_mode_mask,
ati_remote2_store_mode_mask);
static struct attribute *ati_remote2_attrs[] = {
&dev_attr_channel_mask.attr,
&dev_attr_mode_mask.attr,
NULL,
};
static struct attribute_group ati_remote2_attr_group = {
.attrs = ati_remote2_attrs,
};
static int ati_remote2_probe(struct usb_interface *interface, const struct usb_device_id *id)
{
struct usb_device *udev = interface_to_usbdev(interface);
struct usb_host_interface *alt = interface->cur_altsetting;
struct ati_remote2 *ar2;
int r;
if (alt->desc.bInterfaceNumber)
return -ENODEV;
ar2 = kzalloc(sizeof (struct ati_remote2), GFP_KERNEL);
if (!ar2)
return -ENOMEM;
ar2->udev = udev;
ar2->intf[0] = interface;
ar2->ep[0] = &alt->endpoint[0].desc;
ar2->intf[1] = usb_ifnum_to_if(udev, 1);
r = usb_driver_claim_interface(&ati_remote2_driver, ar2->intf[1], ar2);
if (r)
goto fail1;
alt = ar2->intf[1]->cur_altsetting;
ar2->ep[1] = &alt->endpoint[0].desc;
r = ati_remote2_urb_init(ar2);
if (r)
goto fail2;
ar2->channel_mask = channel_mask;
ar2->mode_mask = mode_mask;
r = ati_remote2_setup(ar2, ar2->channel_mask);
if (r)
goto fail2;
usb_make_path(udev, ar2->phys, sizeof(ar2->phys));
strlcat(ar2->phys, "/input0", sizeof(ar2->phys));
strlcat(ar2->name, "ATI Remote Wonder II", sizeof(ar2->name));
r = sysfs_create_group(&udev->dev.kobj, &ati_remote2_attr_group);
if (r)
goto fail2;
r = ati_remote2_input_init(ar2);
if (r)
goto fail3;
usb_set_intfdata(interface, ar2);
interface->needs_remote_wakeup = 1;
return 0;
fail3:
sysfs_remove_group(&udev->dev.kobj, &ati_remote2_attr_group);
fail2:
ati_remote2_urb_cleanup(ar2);
usb_driver_release_interface(&ati_remote2_driver, ar2->intf[1]);
fail1:
kfree(ar2);
return r;
}
static void ati_remote2_disconnect(struct usb_interface *interface)
{
struct ati_remote2 *ar2;
struct usb_host_interface *alt = interface->cur_altsetting;
if (alt->desc.bInterfaceNumber)
return;
ar2 = usb_get_intfdata(interface);
usb_set_intfdata(interface, NULL);
input_unregister_device(ar2->idev);
sysfs_remove_group(&ar2->udev->dev.kobj, &ati_remote2_attr_group);
ati_remote2_urb_cleanup(ar2);
usb_driver_release_interface(&ati_remote2_driver, ar2->intf[1]);
kfree(ar2);
}
static int ati_remote2_suspend(struct usb_interface *interface,
pm_message_t message)
{
struct ati_remote2 *ar2;
struct usb_host_interface *alt = interface->cur_altsetting;
if (alt->desc.bInterfaceNumber)
return 0;
ar2 = usb_get_intfdata(interface);
dev_dbg(&ar2->intf[0]->dev, "%s()\n", __func__);
mutex_lock(&ati_remote2_mutex);
if (ar2->flags & ATI_REMOTE2_OPENED)
ati_remote2_kill_urbs(ar2);
ar2->flags |= ATI_REMOTE2_SUSPENDED;
mutex_unlock(&ati_remote2_mutex);
return 0;
}
static int ati_remote2_resume(struct usb_interface *interface)
{
struct ati_remote2 *ar2;
struct usb_host_interface *alt = interface->cur_altsetting;
int r = 0;
if (alt->desc.bInterfaceNumber)
return 0;
ar2 = usb_get_intfdata(interface);
dev_dbg(&ar2->intf[0]->dev, "%s()\n", __func__);
mutex_lock(&ati_remote2_mutex);
if (ar2->flags & ATI_REMOTE2_OPENED)
r = ati_remote2_submit_urbs(ar2);
if (!r)
ar2->flags &= ~ATI_REMOTE2_SUSPENDED;
mutex_unlock(&ati_remote2_mutex);
return r;
}
static int ati_remote2_reset_resume(struct usb_interface *interface)
{
struct ati_remote2 *ar2;
struct usb_host_interface *alt = interface->cur_altsetting;
int r = 0;
if (alt->desc.bInterfaceNumber)
return 0;
ar2 = usb_get_intfdata(interface);
dev_dbg(&ar2->intf[0]->dev, "%s()\n", __func__);
mutex_lock(&ati_remote2_mutex);
r = ati_remote2_setup(ar2, ar2->channel_mask);
if (r)
goto out;
if (ar2->flags & ATI_REMOTE2_OPENED)
r = ati_remote2_submit_urbs(ar2);
if (!r)
ar2->flags &= ~ATI_REMOTE2_SUSPENDED;
out:
mutex_unlock(&ati_remote2_mutex);
return r;
}
static int ati_remote2_pre_reset(struct usb_interface *interface)
{
struct ati_remote2 *ar2;
struct usb_host_interface *alt = interface->cur_altsetting;
if (alt->desc.bInterfaceNumber)
return 0;
ar2 = usb_get_intfdata(interface);
dev_dbg(&ar2->intf[0]->dev, "%s()\n", __func__);
mutex_lock(&ati_remote2_mutex);
if (ar2->flags == ATI_REMOTE2_OPENED)
ati_remote2_kill_urbs(ar2);
return 0;
}
static int ati_remote2_post_reset(struct usb_interface *interface)
{
struct ati_remote2 *ar2;
struct usb_host_interface *alt = interface->cur_altsetting;
int r = 0;
if (alt->desc.bInterfaceNumber)
return 0;
ar2 = usb_get_intfdata(interface);
dev_dbg(&ar2->intf[0]->dev, "%s()\n", __func__);
if (ar2->flags == ATI_REMOTE2_OPENED)
r = ati_remote2_submit_urbs(ar2);
mutex_unlock(&ati_remote2_mutex);
return r;
}
static int __init ati_remote2_init(void)
{
int r;
r = usb_register(&ati_remote2_driver);
if (r)
printk(KERN_ERR "ati_remote2: usb_register() = %d\n", r);
else
printk(KERN_INFO "ati_remote2: " DRIVER_DESC " " DRIVER_VERSION "\n");
return r;
}
static void __exit ati_remote2_exit(void)
{
usb_deregister(&ati_remote2_driver);
}
module_init(ati_remote2_init);
module_exit(ati_remote2_exit);

View File

@@ -0,0 +1,182 @@
/*
* atlas_btns.c - Atlas Wallmount Touchscreen ACPI Extras
*
* Copyright (C) 2006 Jaya Kumar
* Based on Toshiba ACPI by John Belmonte and ASUS ACPI
* This work was sponsored by CIS(M) Sdn Bhd.
*
* 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
*
*/
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/init.h>
#include <linux/input.h>
#include <linux/types.h>
#include <asm/uaccess.h>
#include <acpi/acpi_drivers.h>
#define ACPI_ATLAS_NAME "Atlas ACPI"
#define ACPI_ATLAS_CLASS "Atlas"
static unsigned short atlas_keymap[16];
static struct input_dev *input_dev;
/* button handling code */
static acpi_status acpi_atlas_button_setup(acpi_handle region_handle,
u32 function, void *handler_context, void **return_context)
{
*return_context =
(function != ACPI_REGION_DEACTIVATE) ? handler_context : NULL;
return AE_OK;
}
static acpi_status acpi_atlas_button_handler(u32 function,
acpi_physical_address address,
u32 bit_width, acpi_integer *value,
void *handler_context, void *region_context)
{
acpi_status status;
if (function == ACPI_WRITE) {
int code = address & 0x0f;
int key_down = !(address & 0x10);
input_event(input_dev, EV_MSC, MSC_SCAN, code);
input_report_key(input_dev, atlas_keymap[code], key_down);
input_sync(input_dev);
status = 0;
} else {
printk(KERN_WARNING "atlas: shrugged on unexpected function"
":function=%x,address=%lx,value=%x\n",
function, (unsigned long)address, (u32)*value);
status = -EINVAL;
}
return status;
}
static int atlas_acpi_button_add(struct acpi_device *device)
{
acpi_status status;
int i;
int err;
input_dev = input_allocate_device();
if (!input_dev) {
printk(KERN_ERR "atlas: unable to allocate input device\n");
return -ENOMEM;
}
input_dev->name = "Atlas ACPI button driver";
input_dev->phys = "ASIM0000/atlas/input0";
input_dev->id.bustype = BUS_HOST;
input_dev->keycode = atlas_keymap;
input_dev->keycodesize = sizeof(unsigned short);
input_dev->keycodemax = ARRAY_SIZE(atlas_keymap);
input_set_capability(input_dev, EV_MSC, MSC_SCAN);
__set_bit(EV_KEY, input_dev->evbit);
for (i = 0; i < ARRAY_SIZE(atlas_keymap); i++) {
if (i < 9) {
atlas_keymap[i] = KEY_F1 + i;
__set_bit(KEY_F1 + i, input_dev->keybit);
} else
atlas_keymap[i] = KEY_RESERVED;
}
err = input_register_device(input_dev);
if (err) {
printk(KERN_ERR "atlas: couldn't register input device\n");
input_free_device(input_dev);
return err;
}
/* hookup button handler */
status = acpi_install_address_space_handler(device->handle,
0x81, &acpi_atlas_button_handler,
&acpi_atlas_button_setup, device);
if (ACPI_FAILURE(status)) {
printk(KERN_ERR "Atlas: Error installing addr spc handler\n");
input_unregister_device(input_dev);
status = -EINVAL;
}
return status;
}
static int atlas_acpi_button_remove(struct acpi_device *device, int type)
{
acpi_status status;
status = acpi_remove_address_space_handler(device->handle,
0x81, &acpi_atlas_button_handler);
if (ACPI_FAILURE(status)) {
printk(KERN_ERR "Atlas: Error removing addr spc handler\n");
status = -EINVAL;
}
input_unregister_device(input_dev);
return status;
}
static const struct acpi_device_id atlas_device_ids[] = {
{"ASIM0000", 0},
{"", 0},
};
MODULE_DEVICE_TABLE(acpi, atlas_device_ids);
static struct acpi_driver atlas_acpi_driver = {
.name = ACPI_ATLAS_NAME,
.class = ACPI_ATLAS_CLASS,
.ids = atlas_device_ids,
.ops = {
.add = atlas_acpi_button_add,
.remove = atlas_acpi_button_remove,
},
};
static int __init atlas_acpi_init(void)
{
int result;
if (acpi_disabled)
return -ENODEV;
result = acpi_bus_register_driver(&atlas_acpi_driver);
if (result < 0) {
printk(KERN_ERR "Atlas ACPI: Unable to register driver\n");
return -ENODEV;
}
return 0;
}
static void __exit atlas_acpi_exit(void)
{
acpi_bus_unregister_driver(&atlas_acpi_driver);
}
module_init(atlas_acpi_init);
module_exit(atlas_acpi_exit);
MODULE_AUTHOR("Jaya Kumar");
MODULE_LICENSE("GPL");
MODULE_DESCRIPTION("Atlas button driver");

View File

@@ -0,0 +1,283 @@
/*
* Rotary counter driver for Analog Devices Blackfin Processors
*
* Copyright 2008-2009 Analog Devices Inc.
* Licensed under the GPL-2 or later.
*/
#include <linux/module.h>
#include <linux/version.h>
#include <linux/init.h>
#include <linux/interrupt.h>
#include <linux/irq.h>
#include <linux/pm.h>
#include <linux/platform_device.h>
#include <linux/input.h>
#include <asm/portmux.h>
#include <asm/bfin_rotary.h>
static const u16 per_cnt[] = {
P_CNT_CUD,
P_CNT_CDG,
P_CNT_CZM,
0
};
struct bfin_rot {
struct input_dev *input;
int irq;
unsigned int up_key;
unsigned int down_key;
unsigned int button_key;
unsigned int rel_code;
unsigned short cnt_config;
unsigned short cnt_imask;
unsigned short cnt_debounce;
};
static void report_key_event(struct input_dev *input, int keycode)
{
/* simulate a press-n-release */
input_report_key(input, keycode, 1);
input_sync(input);
input_report_key(input, keycode, 0);
input_sync(input);
}
static void report_rotary_event(struct bfin_rot *rotary, int delta)
{
struct input_dev *input = rotary->input;
if (rotary->up_key) {
report_key_event(input,
delta > 0 ? rotary->up_key : rotary->down_key);
} else {
input_report_rel(input, rotary->rel_code, delta);
input_sync(input);
}
}
static irqreturn_t bfin_rotary_isr(int irq, void *dev_id)
{
struct platform_device *pdev = dev_id;
struct bfin_rot *rotary = platform_get_drvdata(pdev);
int delta;
switch (bfin_read_CNT_STATUS()) {
case ICII:
break;
case UCII:
case DCII:
delta = bfin_read_CNT_COUNTER();
if (delta)
report_rotary_event(rotary, delta);
break;
case CZMII:
report_key_event(rotary->input, rotary->button_key);
break;
default:
break;
}
bfin_write_CNT_COMMAND(W1LCNT_ZERO); /* Clear COUNTER */
bfin_write_CNT_STATUS(-1); /* Clear STATUS */
return IRQ_HANDLED;
}
static int __devinit bfin_rotary_probe(struct platform_device *pdev)
{
struct bfin_rotary_platform_data *pdata = pdev->dev.platform_data;
struct bfin_rot *rotary;
struct input_dev *input;
int error;
/* Basic validation */
if ((pdata->rotary_up_key && !pdata->rotary_down_key) ||
(!pdata->rotary_up_key && pdata->rotary_down_key)) {
return -EINVAL;
}
error = peripheral_request_list(per_cnt, dev_name(&pdev->dev));
if (error) {
dev_err(&pdev->dev, "requesting peripherals failed\n");
return error;
}
rotary = kzalloc(sizeof(struct bfin_rot), GFP_KERNEL);
input = input_allocate_device();
if (!rotary || !input) {
error = -ENOMEM;
goto out1;
}
rotary->input = input;
rotary->up_key = pdata->rotary_up_key;
rotary->down_key = pdata->rotary_down_key;
rotary->button_key = pdata->rotary_button_key;
rotary->rel_code = pdata->rotary_rel_code;
error = rotary->irq = platform_get_irq(pdev, 0);
if (error < 0)
goto out1;
input->name = pdev->name;
input->phys = "bfin-rotary/input0";
input->dev.parent = &pdev->dev;
input_set_drvdata(input, rotary);
input->id.bustype = BUS_HOST;
input->id.vendor = 0x0001;
input->id.product = 0x0001;
input->id.version = 0x0100;
if (rotary->up_key) {
__set_bit(EV_KEY, input->evbit);
__set_bit(rotary->up_key, input->keybit);
__set_bit(rotary->down_key, input->keybit);
} else {
__set_bit(EV_REL, input->evbit);
__set_bit(rotary->rel_code, input->relbit);
}
if (rotary->button_key) {
__set_bit(EV_KEY, input->evbit);
__set_bit(rotary->button_key, input->keybit);
}
error = request_irq(rotary->irq, bfin_rotary_isr,
0, dev_name(&pdev->dev), pdev);
if (error) {
dev_err(&pdev->dev,
"unable to claim irq %d; error %d\n",
rotary->irq, error);
goto out1;
}
error = input_register_device(input);
if (error) {
dev_err(&pdev->dev,
"unable to register input device (%d)\n", error);
goto out2;
}
if (pdata->rotary_button_key)
bfin_write_CNT_IMASK(CZMIE);
if (pdata->mode & ROT_DEBE)
bfin_write_CNT_DEBOUNCE(pdata->debounce & DPRESCALE);
if (pdata->mode)
bfin_write_CNT_CONFIG(bfin_read_CNT_CONFIG() |
(pdata->mode & ~CNTE));
bfin_write_CNT_IMASK(bfin_read_CNT_IMASK() | UCIE | DCIE);
bfin_write_CNT_CONFIG(bfin_read_CNT_CONFIG() | CNTE);
platform_set_drvdata(pdev, rotary);
device_init_wakeup(&pdev->dev, 1);
return 0;
out2:
free_irq(rotary->irq, pdev);
out1:
input_free_device(input);
kfree(rotary);
peripheral_free_list(per_cnt);
return error;
}
static int __devexit bfin_rotary_remove(struct platform_device *pdev)
{
struct bfin_rot *rotary = platform_get_drvdata(pdev);
bfin_write_CNT_CONFIG(0);
bfin_write_CNT_IMASK(0);
free_irq(rotary->irq, pdev);
input_unregister_device(rotary->input);
peripheral_free_list(per_cnt);
kfree(rotary);
platform_set_drvdata(pdev, NULL);
return 0;
}
#ifdef CONFIG_PM
static int bfin_rotary_suspend(struct device *dev)
{
struct platform_device *pdev = to_platform_device(dev);
struct bfin_rot *rotary = platform_get_drvdata(pdev);
rotary->cnt_config = bfin_read_CNT_CONFIG();
rotary->cnt_imask = bfin_read_CNT_IMASK();
rotary->cnt_debounce = bfin_read_CNT_DEBOUNCE();
if (device_may_wakeup(&pdev->dev))
enable_irq_wake(rotary->irq);
return 0;
}
static int bfin_rotary_resume(struct device *dev)
{
struct platform_device *pdev = to_platform_device(dev);
struct bfin_rot *rotary = platform_get_drvdata(pdev);
bfin_write_CNT_DEBOUNCE(rotary->cnt_debounce);
bfin_write_CNT_IMASK(rotary->cnt_imask);
bfin_write_CNT_CONFIG(rotary->cnt_config & ~CNTE);
if (device_may_wakeup(&pdev->dev))
disable_irq_wake(rotary->irq);
if (rotary->cnt_config & CNTE)
bfin_write_CNT_CONFIG(rotary->cnt_config);
return 0;
}
static struct dev_pm_ops bfin_rotary_pm_ops = {
.suspend = bfin_rotary_suspend,
.resume = bfin_rotary_resume,
};
#endif
static struct platform_driver bfin_rotary_device_driver = {
.probe = bfin_rotary_probe,
.remove = __devexit_p(bfin_rotary_remove),
.driver = {
.name = "bfin-rotary",
.owner = THIS_MODULE,
#ifdef CONFIG_PM
.pm = &bfin_rotary_pm_ops,
#endif
},
};
static int __init bfin_rotary_init(void)
{
return platform_driver_register(&bfin_rotary_device_driver);
}
module_init(bfin_rotary_init);
static void __exit bfin_rotary_exit(void)
{
platform_driver_unregister(&bfin_rotary_device_driver);
}
module_exit(bfin_rotary_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Michael Hennerich <hennerich@blackfin.uclinux.org>");
MODULE_DESCRIPTION("Rotary Counter driver for Blackfin Processors");
MODULE_ALIAS("platform:bfin-rotary");

View File

@@ -0,0 +1,917 @@
/*
* Driver for the VoIP USB phones with CM109 chipsets.
*
* Copyright (C) 2007 - 2008 Alfred E. Heggestad <aeh@db.org>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
* published by the Free Software Foundation, version 2.
*/
/*
* Tested devices:
* - Komunikate KIP1000
* - Genius G-talk
* - Allied-Telesis Corega USBPH01
* - ...
*
* This driver is based on the yealink.c driver
*
* Thanks to:
* - Authors of yealink.c
* - Thomas Reitmayr
* - Oliver Neukum for good review comments and code
* - Shaun Jackman <sjackman@gmail.com> for Genius G-talk keymap
* - Dmitry Torokhov for valuable input and review
*
* Todo:
* - Read/write EEPROM
*/
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/slab.h>
#include <linux/module.h>
#include <linux/moduleparam.h>
#include <linux/rwsem.h>
#include <linux/usb/input.h>
#define DRIVER_VERSION "20080805"
#define DRIVER_AUTHOR "Alfred E. Heggestad"
#define DRIVER_DESC "CM109 phone driver"
static char *phone = "kip1000";
module_param(phone, charp, S_IRUSR);
MODULE_PARM_DESC(phone, "Phone name {kip1000, gtalk, usbph01, atcom}");
enum {
/* HID Registers */
HID_IR0 = 0x00, /* Record/Playback-mute button, Volume up/down */
HID_IR1 = 0x01, /* GPI, generic registers or EEPROM_DATA0 */
HID_IR2 = 0x02, /* Generic registers or EEPROM_DATA1 */
HID_IR3 = 0x03, /* Generic registers or EEPROM_CTRL */
HID_OR0 = 0x00, /* Mapping control, buzzer, SPDIF (offset 0x04) */
HID_OR1 = 0x01, /* GPO - General Purpose Output */
HID_OR2 = 0x02, /* Set GPIO to input/output mode */
HID_OR3 = 0x03, /* SPDIF status channel or EEPROM_CTRL */
/* HID_IR0 */
RECORD_MUTE = 1 << 3,
PLAYBACK_MUTE = 1 << 2,
VOLUME_DOWN = 1 << 1,
VOLUME_UP = 1 << 0,
/* HID_OR0 */
/* bits 7-6
0: HID_OR1-2 are used for GPO; HID_OR0, 3 are used for buzzer
and SPDIF
1: HID_OR0-3 are used as generic HID registers
2: Values written to HID_OR0-3 are also mapped to MCU_CTRL,
EEPROM_DATA0-1, EEPROM_CTRL (see Note)
3: Reserved
*/
HID_OR_GPO_BUZ_SPDIF = 0 << 6,
HID_OR_GENERIC_HID_REG = 1 << 6,
HID_OR_MAP_MCU_EEPROM = 2 << 6,
BUZZER_ON = 1 << 5,
/* up to 256 normal keys, up to 16 special keys */
KEYMAP_SIZE = 256 + 16,
};
/* CM109 protocol packet */
struct cm109_ctl_packet {
u8 byte[4];
} __attribute__ ((packed));
enum { USB_PKT_LEN = sizeof(struct cm109_ctl_packet) };
/* CM109 device structure */
struct cm109_dev {
struct input_dev *idev; /* input device */
struct usb_device *udev; /* usb device */
struct usb_interface *intf;
/* irq input channel */
struct cm109_ctl_packet *irq_data;
dma_addr_t irq_dma;
struct urb *urb_irq;
/* control output channel */
struct cm109_ctl_packet *ctl_data;
dma_addr_t ctl_dma;
struct usb_ctrlrequest *ctl_req;
dma_addr_t ctl_req_dma;
struct urb *urb_ctl;
/*
* The 3 bitfields below are protected by ctl_submit_lock.
* They have to be separate since they are accessed from IRQ
* context.
*/
unsigned irq_urb_pending:1; /* irq_urb is in flight */
unsigned ctl_urb_pending:1; /* ctl_urb is in flight */
unsigned buzzer_pending:1; /* need to issue buzz command */
spinlock_t ctl_submit_lock;
unsigned char buzzer_state; /* on/off */
/* flags */
unsigned open:1;
unsigned resetting:1;
unsigned shutdown:1;
/* This mutex protects writes to the above flags */
struct mutex pm_mutex;
unsigned short keymap[KEYMAP_SIZE];
char phys[64]; /* physical device path */
int key_code; /* last reported key */
int keybit; /* 0=new scan 1,2,4,8=scan columns */
u8 gpi; /* Cached value of GPI (high nibble) */
};
/******************************************************************************
* CM109 key interface
*****************************************************************************/
static unsigned short special_keymap(int code)
{
if (code > 0xff) {
switch (code - 0xff) {
case RECORD_MUTE: return KEY_MUTE;
case PLAYBACK_MUTE: return KEY_MUTE;
case VOLUME_DOWN: return KEY_VOLUMEDOWN;
case VOLUME_UP: return KEY_VOLUMEUP;
}
}
return KEY_RESERVED;
}
/* Map device buttons to internal key events.
*
* The "up" and "down" keys, are symbolised by arrows on the button.
* The "pickup" and "hangup" keys are symbolised by a green and red phone
* on the button.
Komunikate KIP1000 Keyboard Matrix
-> -- 1 -- 2 -- 3 --> GPI pin 4 (0x10)
| | | |
<- -- 4 -- 5 -- 6 --> GPI pin 5 (0x20)
| | | |
END - 7 -- 8 -- 9 --> GPI pin 6 (0x40)
| | | |
OK -- * -- 0 -- # --> GPI pin 7 (0x80)
| | | |
/|\ /|\ /|\ /|\
| | | |
GPO
pin: 3 2 1 0
0x8 0x4 0x2 0x1
*/
static unsigned short keymap_kip1000(int scancode)
{
switch (scancode) { /* phone key: */
case 0x82: return KEY_NUMERIC_0; /* 0 */
case 0x14: return KEY_NUMERIC_1; /* 1 */
case 0x12: return KEY_NUMERIC_2; /* 2 */
case 0x11: return KEY_NUMERIC_3; /* 3 */
case 0x24: return KEY_NUMERIC_4; /* 4 */
case 0x22: return KEY_NUMERIC_5; /* 5 */
case 0x21: return KEY_NUMERIC_6; /* 6 */
case 0x44: return KEY_NUMERIC_7; /* 7 */
case 0x42: return KEY_NUMERIC_8; /* 8 */
case 0x41: return KEY_NUMERIC_9; /* 9 */
case 0x81: return KEY_NUMERIC_POUND; /* # */
case 0x84: return KEY_NUMERIC_STAR; /* * */
case 0x88: return KEY_ENTER; /* pickup */
case 0x48: return KEY_ESC; /* hangup */
case 0x28: return KEY_LEFT; /* IN */
case 0x18: return KEY_RIGHT; /* OUT */
default: return special_keymap(scancode);
}
}
/*
Contributed by Shaun Jackman <sjackman@gmail.com>
Genius G-Talk keyboard matrix
0 1 2 3
4: 0 4 8 Talk
5: 1 5 9 End
6: 2 6 # Up
7: 3 7 * Down
*/
static unsigned short keymap_gtalk(int scancode)
{
switch (scancode) {
case 0x11: return KEY_NUMERIC_0;
case 0x21: return KEY_NUMERIC_1;
case 0x41: return KEY_NUMERIC_2;
case 0x81: return KEY_NUMERIC_3;
case 0x12: return KEY_NUMERIC_4;
case 0x22: return KEY_NUMERIC_5;
case 0x42: return KEY_NUMERIC_6;
case 0x82: return KEY_NUMERIC_7;
case 0x14: return KEY_NUMERIC_8;
case 0x24: return KEY_NUMERIC_9;
case 0x44: return KEY_NUMERIC_POUND; /* # */
case 0x84: return KEY_NUMERIC_STAR; /* * */
case 0x18: return KEY_ENTER; /* Talk (green handset) */
case 0x28: return KEY_ESC; /* End (red handset) */
case 0x48: return KEY_UP; /* Menu up (rocker switch) */
case 0x88: return KEY_DOWN; /* Menu down (rocker switch) */
default: return special_keymap(scancode);
}
}
/*
* Keymap for Allied-Telesis Corega USBPH01
* http://www.alliedtelesis-corega.com/2/1344/1437/1360/chprd.html
*
* Contributed by july@nat.bg
*/
static unsigned short keymap_usbph01(int scancode)
{
switch (scancode) {
case 0x11: return KEY_NUMERIC_0; /* 0 */
case 0x21: return KEY_NUMERIC_1; /* 1 */
case 0x41: return KEY_NUMERIC_2; /* 2 */
case 0x81: return KEY_NUMERIC_3; /* 3 */
case 0x12: return KEY_NUMERIC_4; /* 4 */
case 0x22: return KEY_NUMERIC_5; /* 5 */
case 0x42: return KEY_NUMERIC_6; /* 6 */
case 0x82: return KEY_NUMERIC_7; /* 7 */
case 0x14: return KEY_NUMERIC_8; /* 8 */
case 0x24: return KEY_NUMERIC_9; /* 9 */
case 0x44: return KEY_NUMERIC_POUND; /* # */
case 0x84: return KEY_NUMERIC_STAR; /* * */
case 0x18: return KEY_ENTER; /* pickup */
case 0x28: return KEY_ESC; /* hangup */
case 0x48: return KEY_LEFT; /* IN */
case 0x88: return KEY_RIGHT; /* OUT */
default: return special_keymap(scancode);
}
}
/*
* Keymap for ATCom AU-100
* http://www.atcom.cn/En_products_AU100.html
* http://www.packetizer.com/products/au100/
* http://www.voip-info.org/wiki/view/AU-100
*
* Contributed by daniel@gimpelevich.san-francisco.ca.us
*/
static unsigned short keymap_atcom(int scancode)
{
switch (scancode) { /* phone key: */
case 0x82: return KEY_NUMERIC_0; /* 0 */
case 0x11: return KEY_NUMERIC_1; /* 1 */
case 0x12: return KEY_NUMERIC_2; /* 2 */
case 0x14: return KEY_NUMERIC_3; /* 3 */
case 0x21: return KEY_NUMERIC_4; /* 4 */
case 0x22: return KEY_NUMERIC_5; /* 5 */
case 0x24: return KEY_NUMERIC_6; /* 6 */
case 0x41: return KEY_NUMERIC_7; /* 7 */
case 0x42: return KEY_NUMERIC_8; /* 8 */
case 0x44: return KEY_NUMERIC_9; /* 9 */
case 0x84: return KEY_NUMERIC_POUND; /* # */
case 0x81: return KEY_NUMERIC_STAR; /* * */
case 0x18: return KEY_ENTER; /* pickup */
case 0x28: return KEY_ESC; /* hangup */
case 0x48: return KEY_LEFT; /* left arrow */
case 0x88: return KEY_RIGHT; /* right arrow */
default: return special_keymap(scancode);
}
}
static unsigned short (*keymap)(int) = keymap_kip1000;
/*
* Completes a request by converting the data into events for the
* input subsystem.
*/
static void report_key(struct cm109_dev *dev, int key)
{
struct input_dev *idev = dev->idev;
if (dev->key_code >= 0) {
/* old key up */
input_report_key(idev, dev->key_code, 0);
}
dev->key_code = key;
if (key >= 0) {
/* new valid key */
input_report_key(idev, key, 1);
}
input_sync(idev);
}
/******************************************************************************
* CM109 usb communication interface
*****************************************************************************/
static void cm109_submit_buzz_toggle(struct cm109_dev *dev)
{
int error;
if (dev->buzzer_state)
dev->ctl_data->byte[HID_OR0] |= BUZZER_ON;
else
dev->ctl_data->byte[HID_OR0] &= ~BUZZER_ON;
error = usb_submit_urb(dev->urb_ctl, GFP_ATOMIC);
if (error)
err("%s: usb_submit_urb (urb_ctl) failed %d", __func__, error);
}
/*
* IRQ handler
*/
static void cm109_urb_irq_callback(struct urb *urb)
{
struct cm109_dev *dev = urb->context;
const int status = urb->status;
int error;
dev_dbg(&urb->dev->dev, "### URB IRQ: [0x%02x 0x%02x 0x%02x 0x%02x] keybit=0x%02x\n",
dev->irq_data->byte[0],
dev->irq_data->byte[1],
dev->irq_data->byte[2],
dev->irq_data->byte[3],
dev->keybit);
if (status) {
if (status == -ESHUTDOWN)
return;
err("%s: urb status %d", __func__, status);
}
/* Special keys */
if (dev->irq_data->byte[HID_IR0] & 0x0f) {
const int code = (dev->irq_data->byte[HID_IR0] & 0x0f);
report_key(dev, dev->keymap[0xff + code]);
}
/* Scan key column */
if (dev->keybit == 0xf) {
/* Any changes ? */
if ((dev->gpi & 0xf0) == (dev->irq_data->byte[HID_IR1] & 0xf0))
goto out;
dev->gpi = dev->irq_data->byte[HID_IR1] & 0xf0;
dev->keybit = 0x1;
} else {
report_key(dev, dev->keymap[dev->irq_data->byte[HID_IR1]]);
dev->keybit <<= 1;
if (dev->keybit > 0x8)
dev->keybit = 0xf;
}
out:
spin_lock(&dev->ctl_submit_lock);
dev->irq_urb_pending = 0;
if (likely(!dev->shutdown)) {
if (dev->buzzer_state)
dev->ctl_data->byte[HID_OR0] |= BUZZER_ON;
else
dev->ctl_data->byte[HID_OR0] &= ~BUZZER_ON;
dev->ctl_data->byte[HID_OR1] = dev->keybit;
dev->ctl_data->byte[HID_OR2] = dev->keybit;
dev->buzzer_pending = 0;
dev->ctl_urb_pending = 1;
error = usb_submit_urb(dev->urb_ctl, GFP_ATOMIC);
if (error)
err("%s: usb_submit_urb (urb_ctl) failed %d",
__func__, error);
}
spin_unlock(&dev->ctl_submit_lock);
}
static void cm109_urb_ctl_callback(struct urb *urb)
{
struct cm109_dev *dev = urb->context;
const int status = urb->status;
int error;
dev_dbg(&urb->dev->dev, "### URB CTL: [0x%02x 0x%02x 0x%02x 0x%02x]\n",
dev->ctl_data->byte[0],
dev->ctl_data->byte[1],
dev->ctl_data->byte[2],
dev->ctl_data->byte[3]);
if (status)
err("%s: urb status %d", __func__, status);
spin_lock(&dev->ctl_submit_lock);
dev->ctl_urb_pending = 0;
if (likely(!dev->shutdown)) {
if (dev->buzzer_pending) {
dev->buzzer_pending = 0;
dev->ctl_urb_pending = 1;
cm109_submit_buzz_toggle(dev);
} else if (likely(!dev->irq_urb_pending)) {
/* ask for key data */
dev->irq_urb_pending = 1;
error = usb_submit_urb(dev->urb_irq, GFP_ATOMIC);
if (error)
err("%s: usb_submit_urb (urb_irq) failed %d",
__func__, error);
}
}
spin_unlock(&dev->ctl_submit_lock);
}
static void cm109_toggle_buzzer_async(struct cm109_dev *dev)
{
unsigned long flags;
spin_lock_irqsave(&dev->ctl_submit_lock, flags);
if (dev->ctl_urb_pending) {
/* URB completion will resubmit */
dev->buzzer_pending = 1;
} else {
dev->ctl_urb_pending = 1;
cm109_submit_buzz_toggle(dev);
}
spin_unlock_irqrestore(&dev->ctl_submit_lock, flags);
}
static void cm109_toggle_buzzer_sync(struct cm109_dev *dev, int on)
{
int error;
if (on)
dev->ctl_data->byte[HID_OR0] |= BUZZER_ON;
else
dev->ctl_data->byte[HID_OR0] &= ~BUZZER_ON;
error = usb_control_msg(dev->udev,
usb_sndctrlpipe(dev->udev, 0),
dev->ctl_req->bRequest,
dev->ctl_req->bRequestType,
le16_to_cpu(dev->ctl_req->wValue),
le16_to_cpu(dev->ctl_req->wIndex),
dev->ctl_data,
USB_PKT_LEN, USB_CTRL_SET_TIMEOUT);
if (error && error != EINTR)
err("%s: usb_control_msg() failed %d", __func__, error);
}
static void cm109_stop_traffic(struct cm109_dev *dev)
{
dev->shutdown = 1;
/*
* Make sure other CPUs see this
*/
smp_wmb();
usb_kill_urb(dev->urb_ctl);
usb_kill_urb(dev->urb_irq);
cm109_toggle_buzzer_sync(dev, 0);
dev->shutdown = 0;
smp_wmb();
}
static void cm109_restore_state(struct cm109_dev *dev)
{
if (dev->open) {
/*
* Restore buzzer state.
* This will also kick regular URB submission
*/
cm109_toggle_buzzer_async(dev);
}
}
/******************************************************************************
* input event interface
*****************************************************************************/
static int cm109_input_open(struct input_dev *idev)
{
struct cm109_dev *dev = input_get_drvdata(idev);
int error;
error = usb_autopm_get_interface(dev->intf);
if (error < 0) {
err("%s - cannot autoresume, result %d",
__func__, error);
return error;
}
mutex_lock(&dev->pm_mutex);
dev->buzzer_state = 0;
dev->key_code = -1; /* no keys pressed */
dev->keybit = 0xf;
/* issue INIT */
dev->ctl_data->byte[HID_OR0] = HID_OR_GPO_BUZ_SPDIF;
dev->ctl_data->byte[HID_OR1] = dev->keybit;
dev->ctl_data->byte[HID_OR2] = dev->keybit;
dev->ctl_data->byte[HID_OR3] = 0x00;
error = usb_submit_urb(dev->urb_ctl, GFP_KERNEL);
if (error)
err("%s: usb_submit_urb (urb_ctl) failed %d", __func__, error);
else
dev->open = 1;
mutex_unlock(&dev->pm_mutex);
if (error)
usb_autopm_put_interface(dev->intf);
return error;
}
static void cm109_input_close(struct input_dev *idev)
{
struct cm109_dev *dev = input_get_drvdata(idev);
mutex_lock(&dev->pm_mutex);
/*
* Once we are here event delivery is stopped so we
* don't need to worry about someone starting buzzer
* again
*/
cm109_stop_traffic(dev);
dev->open = 0;
mutex_unlock(&dev->pm_mutex);
usb_autopm_put_interface(dev->intf);
}
static int cm109_input_ev(struct input_dev *idev, unsigned int type,
unsigned int code, int value)
{
struct cm109_dev *dev = input_get_drvdata(idev);
dev_dbg(&dev->udev->dev,
"input_ev: type=%u code=%u value=%d\n", type, code, value);
if (type != EV_SND)
return -EINVAL;
switch (code) {
case SND_TONE:
case SND_BELL:
dev->buzzer_state = !!value;
if (!dev->resetting)
cm109_toggle_buzzer_async(dev);
return 0;
default:
return -EINVAL;
}
}
/******************************************************************************
* Linux interface and usb initialisation
*****************************************************************************/
struct driver_info {
char *name;
};
static const struct driver_info info_cm109 = {
.name = "CM109 USB driver",
};
enum {
VENDOR_ID = 0x0d8c, /* C-Media Electronics */
PRODUCT_ID_CM109 = 0x000e, /* CM109 defines range 0x0008 - 0x000f */
};
/* table of devices that work with this driver */
static const struct usb_device_id cm109_usb_table[] = {
{
.match_flags = USB_DEVICE_ID_MATCH_DEVICE |
USB_DEVICE_ID_MATCH_INT_INFO,
.idVendor = VENDOR_ID,
.idProduct = PRODUCT_ID_CM109,
.bInterfaceClass = USB_CLASS_HID,
.bInterfaceSubClass = 0,
.bInterfaceProtocol = 0,
.driver_info = (kernel_ulong_t) &info_cm109
},
/* you can add more devices here with product ID 0x0008 - 0x000f */
{ }
};
static void cm109_usb_cleanup(struct cm109_dev *dev)
{
if (dev->ctl_req)
usb_buffer_free(dev->udev, sizeof(*(dev->ctl_req)),
dev->ctl_req, dev->ctl_req_dma);
if (dev->ctl_data)
usb_buffer_free(dev->udev, USB_PKT_LEN,
dev->ctl_data, dev->ctl_dma);
if (dev->irq_data)
usb_buffer_free(dev->udev, USB_PKT_LEN,
dev->irq_data, dev->irq_dma);
usb_free_urb(dev->urb_irq); /* parameter validation in core/urb */
usb_free_urb(dev->urb_ctl); /* parameter validation in core/urb */
kfree(dev);
}
static void cm109_usb_disconnect(struct usb_interface *interface)
{
struct cm109_dev *dev = usb_get_intfdata(interface);
usb_set_intfdata(interface, NULL);
input_unregister_device(dev->idev);
cm109_usb_cleanup(dev);
}
static int cm109_usb_probe(struct usb_interface *intf,
const struct usb_device_id *id)
{
struct usb_device *udev = interface_to_usbdev(intf);
struct driver_info *nfo = (struct driver_info *)id->driver_info;
struct usb_host_interface *interface;
struct usb_endpoint_descriptor *endpoint;
struct cm109_dev *dev;
struct input_dev *input_dev = NULL;
int ret, pipe, i;
int error = -ENOMEM;
interface = intf->cur_altsetting;
endpoint = &interface->endpoint[0].desc;
if (!usb_endpoint_is_int_in(endpoint))
return -ENODEV;
dev = kzalloc(sizeof(*dev), GFP_KERNEL);
if (!dev)
return -ENOMEM;
spin_lock_init(&dev->ctl_submit_lock);
mutex_init(&dev->pm_mutex);
dev->udev = udev;
dev->intf = intf;
dev->idev = input_dev = input_allocate_device();
if (!input_dev)
goto err_out;
/* allocate usb buffers */
dev->irq_data = usb_buffer_alloc(udev, USB_PKT_LEN,
GFP_KERNEL, &dev->irq_dma);
if (!dev->irq_data)
goto err_out;
dev->ctl_data = usb_buffer_alloc(udev, USB_PKT_LEN,
GFP_KERNEL, &dev->ctl_dma);
if (!dev->ctl_data)
goto err_out;
dev->ctl_req = usb_buffer_alloc(udev, sizeof(*(dev->ctl_req)),
GFP_KERNEL, &dev->ctl_req_dma);
if (!dev->ctl_req)
goto err_out;
/* allocate urb structures */
dev->urb_irq = usb_alloc_urb(0, GFP_KERNEL);
if (!dev->urb_irq)
goto err_out;
dev->urb_ctl = usb_alloc_urb(0, GFP_KERNEL);
if (!dev->urb_ctl)
goto err_out;
/* get a handle to the interrupt data pipe */
pipe = usb_rcvintpipe(udev, endpoint->bEndpointAddress);
ret = usb_maxpacket(udev, pipe, usb_pipeout(pipe));
if (ret != USB_PKT_LEN)
err("invalid payload size %d, expected %d", ret, USB_PKT_LEN);
/* initialise irq urb */
usb_fill_int_urb(dev->urb_irq, udev, pipe, dev->irq_data,
USB_PKT_LEN,
cm109_urb_irq_callback, dev, endpoint->bInterval);
dev->urb_irq->transfer_dma = dev->irq_dma;
dev->urb_irq->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;
dev->urb_irq->dev = udev;
/* initialise ctl urb */
dev->ctl_req->bRequestType = USB_TYPE_CLASS | USB_RECIP_INTERFACE |
USB_DIR_OUT;
dev->ctl_req->bRequest = USB_REQ_SET_CONFIGURATION;
dev->ctl_req->wValue = cpu_to_le16(0x200);
dev->ctl_req->wIndex = cpu_to_le16(interface->desc.bInterfaceNumber);
dev->ctl_req->wLength = cpu_to_le16(USB_PKT_LEN);
usb_fill_control_urb(dev->urb_ctl, udev, usb_sndctrlpipe(udev, 0),
(void *)dev->ctl_req, dev->ctl_data, USB_PKT_LEN,
cm109_urb_ctl_callback, dev);
dev->urb_ctl->setup_dma = dev->ctl_req_dma;
dev->urb_ctl->transfer_dma = dev->ctl_dma;
dev->urb_ctl->transfer_flags |= URB_NO_SETUP_DMA_MAP |
URB_NO_TRANSFER_DMA_MAP;
dev->urb_ctl->dev = udev;
/* find out the physical bus location */
usb_make_path(udev, dev->phys, sizeof(dev->phys));
strlcat(dev->phys, "/input0", sizeof(dev->phys));
/* register settings for the input device */
input_dev->name = nfo->name;
input_dev->phys = dev->phys;
usb_to_input_id(udev, &input_dev->id);
input_dev->dev.parent = &intf->dev;
input_set_drvdata(input_dev, dev);
input_dev->open = cm109_input_open;
input_dev->close = cm109_input_close;
input_dev->event = cm109_input_ev;
input_dev->keycode = dev->keymap;
input_dev->keycodesize = sizeof(unsigned char);
input_dev->keycodemax = ARRAY_SIZE(dev->keymap);
input_dev->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_SND);
input_dev->sndbit[0] = BIT_MASK(SND_BELL) | BIT_MASK(SND_TONE);
/* register available key events */
for (i = 0; i < KEYMAP_SIZE; i++) {
unsigned short k = keymap(i);
dev->keymap[i] = k;
__set_bit(k, input_dev->keybit);
}
__clear_bit(KEY_RESERVED, input_dev->keybit);
error = input_register_device(dev->idev);
if (error)
goto err_out;
usb_set_intfdata(intf, dev);
return 0;
err_out:
input_free_device(input_dev);
cm109_usb_cleanup(dev);
return error;
}
static int cm109_usb_suspend(struct usb_interface *intf, pm_message_t message)
{
struct cm109_dev *dev = usb_get_intfdata(intf);
dev_info(&intf->dev, "cm109: usb_suspend (event=%d)\n", message.event);
mutex_lock(&dev->pm_mutex);
cm109_stop_traffic(dev);
mutex_unlock(&dev->pm_mutex);
return 0;
}
static int cm109_usb_resume(struct usb_interface *intf)
{
struct cm109_dev *dev = usb_get_intfdata(intf);
dev_info(&intf->dev, "cm109: usb_resume\n");
mutex_lock(&dev->pm_mutex);
cm109_restore_state(dev);
mutex_unlock(&dev->pm_mutex);
return 0;
}
static int cm109_usb_pre_reset(struct usb_interface *intf)
{
struct cm109_dev *dev = usb_get_intfdata(intf);
mutex_lock(&dev->pm_mutex);
/*
* Make sure input events don't try to toggle buzzer
* while we are resetting
*/
dev->resetting = 1;
smp_wmb();
cm109_stop_traffic(dev);
return 0;
}
static int cm109_usb_post_reset(struct usb_interface *intf)
{
struct cm109_dev *dev = usb_get_intfdata(intf);
dev->resetting = 0;
smp_wmb();
cm109_restore_state(dev);
mutex_unlock(&dev->pm_mutex);
return 0;
}
static struct usb_driver cm109_driver = {
.name = "cm109",
.probe = cm109_usb_probe,
.disconnect = cm109_usb_disconnect,
.suspend = cm109_usb_suspend,
.resume = cm109_usb_resume,
.reset_resume = cm109_usb_resume,
.pre_reset = cm109_usb_pre_reset,
.post_reset = cm109_usb_post_reset,
.id_table = cm109_usb_table,
.supports_autosuspend = 1,
};
static int __init cm109_select_keymap(void)
{
/* Load the phone keymap */
if (!strcasecmp(phone, "kip1000")) {
keymap = keymap_kip1000;
printk(KERN_INFO KBUILD_MODNAME ": "
"Keymap for Komunikate KIP1000 phone loaded\n");
} else if (!strcasecmp(phone, "gtalk")) {
keymap = keymap_gtalk;
printk(KERN_INFO KBUILD_MODNAME ": "
"Keymap for Genius G-talk phone loaded\n");
} else if (!strcasecmp(phone, "usbph01")) {
keymap = keymap_usbph01;
printk(KERN_INFO KBUILD_MODNAME ": "
"Keymap for Allied-Telesis Corega USBPH01 phone loaded\n");
} else if (!strcasecmp(phone, "atcom")) {
keymap = keymap_atcom;
printk(KERN_INFO KBUILD_MODNAME ": "
"Keymap for ATCom AU-100 phone loaded\n");
} else {
printk(KERN_ERR KBUILD_MODNAME ": "
"Unsupported phone: %s\n", phone);
return -EINVAL;
}
return 0;
}
static int __init cm109_init(void)
{
int err;
err = cm109_select_keymap();
if (err)
return err;
err = usb_register(&cm109_driver);
if (err)
return err;
printk(KERN_INFO KBUILD_MODNAME ": "
DRIVER_DESC ": " DRIVER_VERSION " (C) " DRIVER_AUTHOR "\n");
return 0;
}
static void __exit cm109_exit(void)
{
usb_deregister(&cm109_driver);
}
module_init(cm109_init);
module_exit(cm109_exit);
MODULE_DEVICE_TABLE(usb, cm109_usb_table);
MODULE_AUTHOR(DRIVER_AUTHOR);
MODULE_DESCRIPTION(DRIVER_DESC);
MODULE_LICENSE("GPL");

View File

@@ -0,0 +1,177 @@
/*
* Cobalt button interface driver.
*
* Copyright (C) 2007-2008 Yoichi Yuasa <yuasa@linux-mips.org>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include <linux/init.h>
#include <linux/input-polldev.h>
#include <linux/ioport.h>
#include <linux/module.h>
#include <linux/platform_device.h>
#define BUTTONS_POLL_INTERVAL 30 /* msec */
#define BUTTONS_COUNT_THRESHOLD 3
#define BUTTONS_STATUS_MASK 0xfe000000
static const unsigned short cobalt_map[] = {
KEY_RESERVED,
KEY_RESTART,
KEY_LEFT,
KEY_UP,
KEY_DOWN,
KEY_RIGHT,
KEY_ENTER,
KEY_SELECT
};
struct buttons_dev {
struct input_polled_dev *poll_dev;
unsigned short keymap[ARRAY_SIZE(cobalt_map)];
int count[ARRAY_SIZE(cobalt_map)];
void __iomem *reg;
};
static void handle_buttons(struct input_polled_dev *dev)
{
struct buttons_dev *bdev = dev->private;
struct input_dev *input = dev->input;
uint32_t status;
int i;
status = ~readl(bdev->reg) >> 24;
for (i = 0; i < ARRAY_SIZE(bdev->keymap); i++) {
if (status & (1U << i)) {
if (++bdev->count[i] == BUTTONS_COUNT_THRESHOLD) {
input_event(input, EV_MSC, MSC_SCAN, i);
input_report_key(input, bdev->keymap[i], 1);
input_sync(input);
}
} else {
if (bdev->count[i] >= BUTTONS_COUNT_THRESHOLD) {
input_event(input, EV_MSC, MSC_SCAN, i);
input_report_key(input, bdev->keymap[i], 0);
input_sync(input);
}
bdev->count[i] = 0;
}
}
}
static int __devinit cobalt_buttons_probe(struct platform_device *pdev)
{
struct buttons_dev *bdev;
struct input_polled_dev *poll_dev;
struct input_dev *input;
struct resource *res;
int error, i;
bdev = kzalloc(sizeof(struct buttons_dev), GFP_KERNEL);
poll_dev = input_allocate_polled_device();
if (!bdev || !poll_dev) {
error = -ENOMEM;
goto err_free_mem;
}
memcpy(bdev->keymap, cobalt_map, sizeof(bdev->keymap));
poll_dev->private = bdev;
poll_dev->poll = handle_buttons;
poll_dev->poll_interval = BUTTONS_POLL_INTERVAL;
input = poll_dev->input;
input->name = "Cobalt buttons";
input->phys = "cobalt/input0";
input->id.bustype = BUS_HOST;
input->dev.parent = &pdev->dev;
input->keycode = bdev->keymap;
input->keycodemax = ARRAY_SIZE(bdev->keymap);
input->keycodesize = sizeof(unsigned short);
input_set_capability(input, EV_MSC, MSC_SCAN);
__set_bit(EV_KEY, input->evbit);
for (i = 0; i < ARRAY_SIZE(cobalt_map); i++)
__set_bit(bdev->keymap[i], input->keybit);
__clear_bit(KEY_RESERVED, input->keybit);
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
if (!res) {
error = -EBUSY;
goto err_free_mem;
}
bdev->poll_dev = poll_dev;
bdev->reg = ioremap(res->start, resource_size(res));
dev_set_drvdata(&pdev->dev, bdev);
error = input_register_polled_device(poll_dev);
if (error)
goto err_iounmap;
return 0;
err_iounmap:
iounmap(bdev->reg);
err_free_mem:
input_free_polled_device(poll_dev);
kfree(bdev);
dev_set_drvdata(&pdev->dev, NULL);
return error;
}
static int __devexit cobalt_buttons_remove(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
struct buttons_dev *bdev = dev_get_drvdata(dev);
input_unregister_polled_device(bdev->poll_dev);
input_free_polled_device(bdev->poll_dev);
iounmap(bdev->reg);
kfree(bdev);
dev_set_drvdata(dev, NULL);
return 0;
}
MODULE_AUTHOR("Yoichi Yuasa <yuasa@linux-mips.org>");
MODULE_DESCRIPTION("Cobalt button interface driver");
MODULE_LICENSE("GPL");
/* work with hotplug and coldplug */
MODULE_ALIAS("platform:Cobalt buttons");
static struct platform_driver cobalt_buttons_driver = {
.probe = cobalt_buttons_probe,
.remove = __devexit_p(cobalt_buttons_remove),
.driver = {
.name = "Cobalt buttons",
.owner = THIS_MODULE,
},
};
static int __init cobalt_buttons_init(void)
{
return platform_driver_register(&cobalt_buttons_driver);
}
static void __exit cobalt_buttons_exit(void)
{
platform_driver_unregister(&cobalt_buttons_driver);
}
module_init(cobalt_buttons_init);
module_exit(cobalt_buttons_exit);

View File

@@ -0,0 +1,319 @@
/*
* dm355evm_keys.c - support buttons and IR remote on DM355 EVM board
*
* Copyright (c) 2008 by David Brownell
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version
* 2 of the License, or (at your option) any later version.
*/
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/input.h>
#include <linux/platform_device.h>
#include <linux/interrupt.h>
#include <linux/i2c/dm355evm_msp.h>
/*
* The MSP430 firmware on the DM355 EVM monitors on-board pushbuttons
* and an IR receptor used for the remote control. When any key is
* pressed, or its autorepeat kicks in, an event is sent. This driver
* read those events from the small (32 event) queue and reports them.
*
* Note that physically there can only be one of these devices.
*
* This driver was tested with firmware revision A4.
*/
struct dm355evm_keys {
struct input_dev *input;
struct device *dev;
int irq;
};
/* These initial keycodes can be remapped by dm355evm_setkeycode(). */
static struct {
u16 event;
u16 keycode;
} dm355evm_keys[] = {
/*
* Pushbuttons on the EVM board ... note that the labels for these
* are SW10/SW11/etc on the PC board. The left/right orientation
* comes only from the firmware's documentation, and presumes the
* power connector is immediately in front of you and the IR sensor
* is to the right. (That is, rotate the board counter-clockwise
* by 90 degrees from the SW10/etc and "DM355 EVM" labels.)
*/
{ 0x00d8, KEY_OK, }, /* SW12 */
{ 0x00b8, KEY_UP, }, /* SW13 */
{ 0x00e8, KEY_DOWN, }, /* SW11 */
{ 0x0078, KEY_LEFT, }, /* SW14 */
{ 0x00f0, KEY_RIGHT, }, /* SW10 */
/*
* IR buttons ... codes assigned to match the universal remote
* provided with the EVM (Philips PM4S) using DVD code 0020.
*
* These event codes match firmware documentation, but other
* remote controls could easily send more RC5-encoded events.
* The PM4S manual was used in several cases to help select
* a keycode reflecting the intended usage.
*
* RC5 codes are 14 bits, with two start bits (0x3 prefix)
* and a toggle bit (masked out below).
*/
{ 0x300c, KEY_POWER, }, /* NOTE: docs omit this */
{ 0x3000, KEY_NUMERIC_0, },
{ 0x3001, KEY_NUMERIC_1, },
{ 0x3002, KEY_NUMERIC_2, },
{ 0x3003, KEY_NUMERIC_3, },
{ 0x3004, KEY_NUMERIC_4, },
{ 0x3005, KEY_NUMERIC_5, },
{ 0x3006, KEY_NUMERIC_6, },
{ 0x3007, KEY_NUMERIC_7, },
{ 0x3008, KEY_NUMERIC_8, },
{ 0x3009, KEY_NUMERIC_9, },
{ 0x3022, KEY_ENTER, },
{ 0x30ec, KEY_MODE, }, /* "tv/vcr/..." */
{ 0x300f, KEY_SELECT, }, /* "info" */
{ 0x3020, KEY_CHANNELUP, }, /* "up" */
{ 0x302e, KEY_MENU, }, /* "in/out" */
{ 0x3011, KEY_VOLUMEDOWN, }, /* "left" */
{ 0x300d, KEY_MUTE, }, /* "ok" */
{ 0x3010, KEY_VOLUMEUP, }, /* "right" */
{ 0x301e, KEY_SUBTITLE, }, /* "cc" */
{ 0x3021, KEY_CHANNELDOWN, }, /* "down" */
{ 0x3022, KEY_PREVIOUS, },
{ 0x3026, KEY_SLEEP, },
{ 0x3172, KEY_REWIND, }, /* NOTE: docs wrongly say 0x30ca */
{ 0x3175, KEY_PLAY, },
{ 0x3174, KEY_FASTFORWARD, },
{ 0x3177, KEY_RECORD, },
{ 0x3176, KEY_STOP, },
{ 0x3169, KEY_PAUSE, },
};
/*
* Because we communicate with the MSP430 using I2C, and all I2C calls
* in Linux sleep, we use a threaded IRQ handler. The IRQ itself is
* active low, but we go through the GPIO controller so we can trigger
* on falling edges and not worry about enabling/disabling the IRQ in
* the keypress handling path.
*/
static irqreturn_t dm355evm_keys_irq(int irq, void *_keys)
{
struct dm355evm_keys *keys = _keys;
int status;
/* For simplicity we ignore INPUT_COUNT and just read
* events until we get the "queue empty" indicator.
* Reading INPUT_LOW decrements the count.
*/
for (;;) {
static u16 last_event;
u16 event;
int keycode;
int i;
status = dm355evm_msp_read(DM355EVM_MSP_INPUT_HIGH);
if (status < 0) {
dev_dbg(keys->dev, "input high err %d\n",
status);
break;
}
event = status << 8;
status = dm355evm_msp_read(DM355EVM_MSP_INPUT_LOW);
if (status < 0) {
dev_dbg(keys->dev, "input low err %d\n",
status);
break;
}
event |= status;
if (event == 0xdead)
break;
/* Press and release a button: two events, same code.
* Press and hold (autorepeat), then release: N events
* (N > 2), same code. For RC5 buttons the toggle bits
* distinguish (for example) "1-autorepeat" from "1 1";
* but PCB buttons don't support that bit.
*
* So we must synthesize release events. We do that by
* mapping events to a press/release event pair; then
* to avoid adding extra events, skip the second event
* of each pair.
*/
if (event == last_event) {
last_event = 0;
continue;
}
last_event = event;
/* ignore the RC5 toggle bit */
event &= ~0x0800;
/* find the key, or leave it as unknown */
keycode = KEY_UNKNOWN;
for (i = 0; i < ARRAY_SIZE(dm355evm_keys); i++) {
if (dm355evm_keys[i].event != event)
continue;
keycode = dm355evm_keys[i].keycode;
break;
}
dev_dbg(keys->dev,
"input event 0x%04x--> keycode %d\n",
event, keycode);
/* report press + release */
input_report_key(keys->input, keycode, 1);
input_sync(keys->input);
input_report_key(keys->input, keycode, 0);
input_sync(keys->input);
}
return IRQ_HANDLED;
}
static int dm355evm_setkeycode(struct input_dev *dev, int index, int keycode)
{
u16 old_keycode;
unsigned i;
if (((unsigned)index) >= ARRAY_SIZE(dm355evm_keys))
return -EINVAL;
old_keycode = dm355evm_keys[index].keycode;
dm355evm_keys[index].keycode = keycode;
set_bit(keycode, dev->keybit);
for (i = 0; i < ARRAY_SIZE(dm355evm_keys); i++) {
if (dm355evm_keys[index].keycode == old_keycode)
goto done;
}
clear_bit(old_keycode, dev->keybit);
done:
return 0;
}
static int dm355evm_getkeycode(struct input_dev *dev, int index, int *keycode)
{
if (((unsigned)index) >= ARRAY_SIZE(dm355evm_keys))
return -EINVAL;
return dm355evm_keys[index].keycode;
}
/*----------------------------------------------------------------------*/
static int __devinit dm355evm_keys_probe(struct platform_device *pdev)
{
struct dm355evm_keys *keys;
struct input_dev *input;
int status;
int i;
/* allocate instance struct and input dev */
keys = kzalloc(sizeof *keys, GFP_KERNEL);
input = input_allocate_device();
if (!keys || !input) {
status = -ENOMEM;
goto fail1;
}
keys->dev = &pdev->dev;
keys->input = input;
/* set up "threaded IRQ handler" */
status = platform_get_irq(pdev, 0);
if (status < 0)
goto fail1;
keys->irq = status;
input_set_drvdata(input, keys);
input->name = "DM355 EVM Controls";
input->phys = "dm355evm/input0";
input->dev.parent = &pdev->dev;
input->id.bustype = BUS_I2C;
input->id.product = 0x0355;
input->id.version = dm355evm_msp_read(DM355EVM_MSP_FIRMREV);
input->evbit[0] = BIT(EV_KEY);
for (i = 0; i < ARRAY_SIZE(dm355evm_keys); i++)
__set_bit(dm355evm_keys[i].keycode, input->keybit);
input->setkeycode = dm355evm_setkeycode;
input->getkeycode = dm355evm_getkeycode;
/* REVISIT: flush the event queue? */
status = request_threaded_irq(keys->irq, NULL, dm355evm_keys_irq,
IRQF_TRIGGER_FALLING, dev_name(&pdev->dev), keys);
if (status < 0)
goto fail1;
/* register */
status = input_register_device(input);
if (status < 0)
goto fail2;
platform_set_drvdata(pdev, keys);
return 0;
fail2:
free_irq(keys->irq, keys);
fail1:
input_free_device(input);
kfree(keys);
dev_err(&pdev->dev, "can't register, err %d\n", status);
return status;
}
static int __devexit dm355evm_keys_remove(struct platform_device *pdev)
{
struct dm355evm_keys *keys = platform_get_drvdata(pdev);
free_irq(keys->irq, keys);
input_unregister_device(keys->input);
kfree(keys);
return 0;
}
/* REVISIT: add suspend/resume when DaVinci supports it. The IRQ should
* be able to wake up the system. When device_may_wakeup(&pdev->dev), call
* enable_irq_wake() on suspend, and disable_irq_wake() on resume.
*/
/*
* I2C is used to talk to the MSP430, but this platform device is
* exposed by an MFD driver that manages I2C communications.
*/
static struct platform_driver dm355evm_keys_driver = {
.probe = dm355evm_keys_probe,
.remove = __devexit_p(dm355evm_keys_remove),
.driver = {
.owner = THIS_MODULE,
.name = "dm355evm_keys",
},
};
static int __init dm355evm_keys_init(void)
{
return platform_driver_register(&dm355evm_keys_driver);
}
module_init(dm355evm_keys_init);
static void __exit dm355evm_keys_exit(void)
{
platform_driver_unregister(&dm355evm_keys_driver);
}
module_exit(dm355evm_keys_exit);
MODULE_LICENSE("GPL");

View File

@@ -0,0 +1,714 @@
/*
* HP i8042 SDC + MSM-58321 BBRTC driver.
*
* Copyright (c) 2001 Brian S. Julin
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions, and the following disclaimer,
* without modification.
* 2. The name of the author may not be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* Alternatively, this software may be distributed under the terms of the
* GNU General Public License ("GPL").
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR
* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
*
* References:
* System Device Controller Microprocessor Firmware Theory of Operation
* for Part Number 1820-4784 Revision B. Dwg No. A-1820-4784-2
* efirtc.c by Stephane Eranian/Hewlett Packard
*
*/
#include <linux/hp_sdc.h>
#include <linux/errno.h>
#include <linux/smp_lock.h>
#include <linux/types.h>
#include <linux/init.h>
#include <linux/module.h>
#include <linux/time.h>
#include <linux/miscdevice.h>
#include <linux/proc_fs.h>
#include <linux/poll.h>
#include <linux/rtc.h>
#include <linux/semaphore.h>
MODULE_AUTHOR("Brian S. Julin <bri@calyx.com>");
MODULE_DESCRIPTION("HP i8042 SDC + MSM-58321 RTC Driver");
MODULE_LICENSE("Dual BSD/GPL");
#define RTC_VERSION "1.10d"
static unsigned long epoch = 2000;
static struct semaphore i8042tregs;
static hp_sdc_irqhook hp_sdc_rtc_isr;
static struct fasync_struct *hp_sdc_rtc_async_queue;
static DECLARE_WAIT_QUEUE_HEAD(hp_sdc_rtc_wait);
static ssize_t hp_sdc_rtc_read(struct file *file, char __user *buf,
size_t count, loff_t *ppos);
static int hp_sdc_rtc_ioctl(struct inode *inode, struct file *file,
unsigned int cmd, unsigned long arg);
static unsigned int hp_sdc_rtc_poll(struct file *file, poll_table *wait);
static int hp_sdc_rtc_open(struct inode *inode, struct file *file);
static int hp_sdc_rtc_fasync (int fd, struct file *filp, int on);
static int hp_sdc_rtc_read_proc(char *page, char **start, off_t off,
int count, int *eof, void *data);
static void hp_sdc_rtc_isr (int irq, void *dev_id,
uint8_t status, uint8_t data)
{
return;
}
static int hp_sdc_rtc_do_read_bbrtc (struct rtc_time *rtctm)
{
struct semaphore tsem;
hp_sdc_transaction t;
uint8_t tseq[91];
int i;
i = 0;
while (i < 91) {
tseq[i++] = HP_SDC_ACT_DATAREG |
HP_SDC_ACT_POSTCMD | HP_SDC_ACT_DATAIN;
tseq[i++] = 0x01; /* write i8042[0x70] */
tseq[i] = i / 7; /* BBRTC reg address */
i++;
tseq[i++] = HP_SDC_CMD_DO_RTCR; /* Trigger command */
tseq[i++] = 2; /* expect 1 stat/dat pair back. */
i++; i++; /* buffer for stat/dat pair */
}
tseq[84] |= HP_SDC_ACT_SEMAPHORE;
t.endidx = 91;
t.seq = tseq;
t.act.semaphore = &tsem;
init_MUTEX_LOCKED(&tsem);
if (hp_sdc_enqueue_transaction(&t)) return -1;
down_interruptible(&tsem); /* Put ourselves to sleep for results. */
/* Check for nonpresence of BBRTC */
if (!((tseq[83] | tseq[90] | tseq[69] | tseq[76] |
tseq[55] | tseq[62] | tseq[34] | tseq[41] |
tseq[20] | tseq[27] | tseq[6] | tseq[13]) & 0x0f))
return -1;
memset(rtctm, 0, sizeof(struct rtc_time));
rtctm->tm_year = (tseq[83] & 0x0f) + (tseq[90] & 0x0f) * 10;
rtctm->tm_mon = (tseq[69] & 0x0f) + (tseq[76] & 0x0f) * 10;
rtctm->tm_mday = (tseq[55] & 0x0f) + (tseq[62] & 0x0f) * 10;
rtctm->tm_wday = (tseq[48] & 0x0f);
rtctm->tm_hour = (tseq[34] & 0x0f) + (tseq[41] & 0x0f) * 10;
rtctm->tm_min = (tseq[20] & 0x0f) + (tseq[27] & 0x0f) * 10;
rtctm->tm_sec = (tseq[6] & 0x0f) + (tseq[13] & 0x0f) * 10;
return 0;
}
static int hp_sdc_rtc_read_bbrtc (struct rtc_time *rtctm)
{
struct rtc_time tm, tm_last;
int i = 0;
/* MSM-58321 has no read latch, so must read twice and compare. */
if (hp_sdc_rtc_do_read_bbrtc(&tm_last)) return -1;
if (hp_sdc_rtc_do_read_bbrtc(&tm)) return -1;
while (memcmp(&tm, &tm_last, sizeof(struct rtc_time))) {
if (i++ > 4) return -1;
memcpy(&tm_last, &tm, sizeof(struct rtc_time));
if (hp_sdc_rtc_do_read_bbrtc(&tm)) return -1;
}
memcpy(rtctm, &tm, sizeof(struct rtc_time));
return 0;
}
static int64_t hp_sdc_rtc_read_i8042timer (uint8_t loadcmd, int numreg)
{
hp_sdc_transaction t;
uint8_t tseq[26] = {
HP_SDC_ACT_PRECMD | HP_SDC_ACT_POSTCMD | HP_SDC_ACT_DATAIN,
0,
HP_SDC_CMD_READ_T1, 2, 0, 0,
HP_SDC_ACT_POSTCMD | HP_SDC_ACT_DATAIN,
HP_SDC_CMD_READ_T2, 2, 0, 0,
HP_SDC_ACT_POSTCMD | HP_SDC_ACT_DATAIN,
HP_SDC_CMD_READ_T3, 2, 0, 0,
HP_SDC_ACT_POSTCMD | HP_SDC_ACT_DATAIN,
HP_SDC_CMD_READ_T4, 2, 0, 0,
HP_SDC_ACT_POSTCMD | HP_SDC_ACT_DATAIN,
HP_SDC_CMD_READ_T5, 2, 0, 0
};
t.endidx = numreg * 5;
tseq[1] = loadcmd;
tseq[t.endidx - 4] |= HP_SDC_ACT_SEMAPHORE; /* numreg assumed > 1 */
t.seq = tseq;
t.act.semaphore = &i8042tregs;
down_interruptible(&i8042tregs); /* Sleep if output regs in use. */
if (hp_sdc_enqueue_transaction(&t)) return -1;
down_interruptible(&i8042tregs); /* Sleep until results come back. */
up(&i8042tregs);
return (tseq[5] |
((uint64_t)(tseq[10]) << 8) | ((uint64_t)(tseq[15]) << 16) |
((uint64_t)(tseq[20]) << 24) | ((uint64_t)(tseq[25]) << 32));
}
/* Read the i8042 real-time clock */
static inline int hp_sdc_rtc_read_rt(struct timeval *res) {
int64_t raw;
uint32_t tenms;
unsigned int days;
raw = hp_sdc_rtc_read_i8042timer(HP_SDC_CMD_LOAD_RT, 5);
if (raw < 0) return -1;
tenms = (uint32_t)raw & 0xffffff;
days = (unsigned int)(raw >> 24) & 0xffff;
res->tv_usec = (suseconds_t)(tenms % 100) * 10000;
res->tv_sec = (time_t)(tenms / 100) + days * 86400;
return 0;
}
/* Read the i8042 fast handshake timer */
static inline int hp_sdc_rtc_read_fhs(struct timeval *res) {
int64_t raw;
unsigned int tenms;
raw = hp_sdc_rtc_read_i8042timer(HP_SDC_CMD_LOAD_FHS, 2);
if (raw < 0) return -1;
tenms = (unsigned int)raw & 0xffff;
res->tv_usec = (suseconds_t)(tenms % 100) * 10000;
res->tv_sec = (time_t)(tenms / 100);
return 0;
}
/* Read the i8042 match timer (a.k.a. alarm) */
static inline int hp_sdc_rtc_read_mt(struct timeval *res) {
int64_t raw;
uint32_t tenms;
raw = hp_sdc_rtc_read_i8042timer(HP_SDC_CMD_LOAD_MT, 3);
if (raw < 0) return -1;
tenms = (uint32_t)raw & 0xffffff;
res->tv_usec = (suseconds_t)(tenms % 100) * 10000;
res->tv_sec = (time_t)(tenms / 100);
return 0;
}
/* Read the i8042 delay timer */
static inline int hp_sdc_rtc_read_dt(struct timeval *res) {
int64_t raw;
uint32_t tenms;
raw = hp_sdc_rtc_read_i8042timer(HP_SDC_CMD_LOAD_DT, 3);
if (raw < 0) return -1;
tenms = (uint32_t)raw & 0xffffff;
res->tv_usec = (suseconds_t)(tenms % 100) * 10000;
res->tv_sec = (time_t)(tenms / 100);
return 0;
}
/* Read the i8042 cycle timer (a.k.a. periodic) */
static inline int hp_sdc_rtc_read_ct(struct timeval *res) {
int64_t raw;
uint32_t tenms;
raw = hp_sdc_rtc_read_i8042timer(HP_SDC_CMD_LOAD_CT, 3);
if (raw < 0) return -1;
tenms = (uint32_t)raw & 0xffffff;
res->tv_usec = (suseconds_t)(tenms % 100) * 10000;
res->tv_sec = (time_t)(tenms / 100);
return 0;
}
/* Set the i8042 real-time clock */
static int hp_sdc_rtc_set_rt (struct timeval *setto)
{
uint32_t tenms;
unsigned int days;
hp_sdc_transaction t;
uint8_t tseq[11] = {
HP_SDC_ACT_PRECMD | HP_SDC_ACT_DATAOUT,
HP_SDC_CMD_SET_RTMS, 3, 0, 0, 0,
HP_SDC_ACT_PRECMD | HP_SDC_ACT_DATAOUT,
HP_SDC_CMD_SET_RTD, 2, 0, 0
};
t.endidx = 10;
if (0xffff < setto->tv_sec / 86400) return -1;
days = setto->tv_sec / 86400;
if (0xffff < setto->tv_usec / 1000000 / 86400) return -1;
days += ((setto->tv_sec % 86400) + setto->tv_usec / 1000000) / 86400;
if (days > 0xffff) return -1;
if (0xffffff < setto->tv_sec) return -1;
tenms = setto->tv_sec * 100;
if (0xffffff < setto->tv_usec / 10000) return -1;
tenms += setto->tv_usec / 10000;
if (tenms > 0xffffff) return -1;
tseq[3] = (uint8_t)(tenms & 0xff);
tseq[4] = (uint8_t)((tenms >> 8) & 0xff);
tseq[5] = (uint8_t)((tenms >> 16) & 0xff);
tseq[9] = (uint8_t)(days & 0xff);
tseq[10] = (uint8_t)((days >> 8) & 0xff);
t.seq = tseq;
if (hp_sdc_enqueue_transaction(&t)) return -1;
return 0;
}
/* Set the i8042 fast handshake timer */
static int hp_sdc_rtc_set_fhs (struct timeval *setto)
{
uint32_t tenms;
hp_sdc_transaction t;
uint8_t tseq[5] = {
HP_SDC_ACT_PRECMD | HP_SDC_ACT_DATAOUT,
HP_SDC_CMD_SET_FHS, 2, 0, 0
};
t.endidx = 4;
if (0xffff < setto->tv_sec) return -1;
tenms = setto->tv_sec * 100;
if (0xffff < setto->tv_usec / 10000) return -1;
tenms += setto->tv_usec / 10000;
if (tenms > 0xffff) return -1;
tseq[3] = (uint8_t)(tenms & 0xff);
tseq[4] = (uint8_t)((tenms >> 8) & 0xff);
t.seq = tseq;
if (hp_sdc_enqueue_transaction(&t)) return -1;
return 0;
}
/* Set the i8042 match timer (a.k.a. alarm) */
#define hp_sdc_rtc_set_mt (setto) \
hp_sdc_rtc_set_i8042timer(setto, HP_SDC_CMD_SET_MT)
/* Set the i8042 delay timer */
#define hp_sdc_rtc_set_dt (setto) \
hp_sdc_rtc_set_i8042timer(setto, HP_SDC_CMD_SET_DT)
/* Set the i8042 cycle timer (a.k.a. periodic) */
#define hp_sdc_rtc_set_ct (setto) \
hp_sdc_rtc_set_i8042timer(setto, HP_SDC_CMD_SET_CT)
/* Set one of the i8042 3-byte wide timers */
static int hp_sdc_rtc_set_i8042timer (struct timeval *setto, uint8_t setcmd)
{
uint32_t tenms;
hp_sdc_transaction t;
uint8_t tseq[6] = {
HP_SDC_ACT_PRECMD | HP_SDC_ACT_DATAOUT,
0, 3, 0, 0, 0
};
t.endidx = 6;
if (0xffffff < setto->tv_sec) return -1;
tenms = setto->tv_sec * 100;
if (0xffffff < setto->tv_usec / 10000) return -1;
tenms += setto->tv_usec / 10000;
if (tenms > 0xffffff) return -1;
tseq[1] = setcmd;
tseq[3] = (uint8_t)(tenms & 0xff);
tseq[4] = (uint8_t)((tenms >> 8) & 0xff);
tseq[5] = (uint8_t)((tenms >> 16) & 0xff);
t.seq = tseq;
if (hp_sdc_enqueue_transaction(&t)) {
return -1;
}
return 0;
}
static ssize_t hp_sdc_rtc_read(struct file *file, char __user *buf,
size_t count, loff_t *ppos) {
ssize_t retval;
if (count < sizeof(unsigned long))
return -EINVAL;
retval = put_user(68, (unsigned long __user *)buf);
return retval;
}
static unsigned int hp_sdc_rtc_poll(struct file *file, poll_table *wait)
{
unsigned long l;
l = 0;
if (l != 0)
return POLLIN | POLLRDNORM;
return 0;
}
static int hp_sdc_rtc_open(struct inode *inode, struct file *file)
{
cycle_kernel_lock();
return 0;
}
static int hp_sdc_rtc_fasync (int fd, struct file *filp, int on)
{
return fasync_helper (fd, filp, on, &hp_sdc_rtc_async_queue);
}
static int hp_sdc_rtc_proc_output (char *buf)
{
#define YN(bit) ("no")
#define NY(bit) ("yes")
char *p;
struct rtc_time tm;
struct timeval tv;
memset(&tm, 0, sizeof(struct rtc_time));
p = buf;
if (hp_sdc_rtc_read_bbrtc(&tm)) {
p += sprintf(p, "BBRTC\t\t: READ FAILED!\n");
} else {
p += sprintf(p,
"rtc_time\t: %02d:%02d:%02d\n"
"rtc_date\t: %04d-%02d-%02d\n"
"rtc_epoch\t: %04lu\n",
tm.tm_hour, tm.tm_min, tm.tm_sec,
tm.tm_year + 1900, tm.tm_mon + 1,
tm.tm_mday, epoch);
}
if (hp_sdc_rtc_read_rt(&tv)) {
p += sprintf(p, "i8042 rtc\t: READ FAILED!\n");
} else {
p += sprintf(p, "i8042 rtc\t: %ld.%02d seconds\n",
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, (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, (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, (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, (int)tv.tv_usec/1000);
}
p += sprintf(p,
"DST_enable\t: %s\n"
"BCD\t\t: %s\n"
"24hr\t\t: %s\n"
"square_wave\t: %s\n"
"alarm_IRQ\t: %s\n"
"update_IRQ\t: %s\n"
"periodic_IRQ\t: %s\n"
"periodic_freq\t: %ld\n"
"batt_status\t: %s\n",
YN(RTC_DST_EN),
NY(RTC_DM_BINARY),
YN(RTC_24H),
YN(RTC_SQWE),
YN(RTC_AIE),
YN(RTC_UIE),
YN(RTC_PIE),
1UL,
1 ? "okay" : "dead");
return p - buf;
#undef YN
#undef NY
}
static int hp_sdc_rtc_read_proc(char *page, char **start, off_t off,
int count, int *eof, void *data)
{
int len = hp_sdc_rtc_proc_output (page);
if (len <= off+count) *eof = 1;
*start = page + off;
len -= off;
if (len>count) len = count;
if (len<0) len = 0;
return len;
}
static int hp_sdc_rtc_ioctl(struct inode *inode, struct file *file,
unsigned int cmd, unsigned long arg)
{
#if 1
return -EINVAL;
#else
struct rtc_time wtime;
struct timeval ttime;
int use_wtime = 0;
/* This needs major work. */
switch (cmd) {
case RTC_AIE_OFF: /* Mask alarm int. enab. bit */
case RTC_AIE_ON: /* Allow alarm interrupts. */
case RTC_PIE_OFF: /* Mask periodic int. enab. bit */
case RTC_PIE_ON: /* Allow periodic ints */
case RTC_UIE_ON: /* Allow ints for RTC updates. */
case RTC_UIE_OFF: /* Allow ints for RTC updates. */
{
/* We cannot mask individual user timers and we
cannot tell them apart when they occur, so it
would be disingenuous to succeed these IOCTLs */
return -EINVAL;
}
case RTC_ALM_READ: /* Read the present alarm time */
{
if (hp_sdc_rtc_read_mt(&ttime)) return -EFAULT;
if (hp_sdc_rtc_read_bbrtc(&wtime)) return -EFAULT;
wtime.tm_hour = ttime.tv_sec / 3600; ttime.tv_sec %= 3600;
wtime.tm_min = ttime.tv_sec / 60; ttime.tv_sec %= 60;
wtime.tm_sec = ttime.tv_sec;
break;
}
case RTC_IRQP_READ: /* Read the periodic IRQ rate. */
{
return put_user(hp_sdc_rtc_freq, (unsigned long *)arg);
}
case RTC_IRQP_SET: /* Set periodic IRQ rate. */
{
/*
* The max we can do is 100Hz.
*/
if ((arg < 1) || (arg > 100)) return -EINVAL;
ttime.tv_sec = 0;
ttime.tv_usec = 1000000 / arg;
if (hp_sdc_rtc_set_ct(&ttime)) return -EFAULT;
hp_sdc_rtc_freq = arg;
return 0;
}
case RTC_ALM_SET: /* Store a time into the alarm */
{
/*
* This expects a struct hp_sdc_rtc_time. Writing 0xff means
* "don't care" or "match all" for PC timers. The HP SDC
* does not support that perk, but it could be emulated fairly
* easily. Only the tm_hour, tm_min and tm_sec are used.
* We could do it with 10ms accuracy with the HP SDC, if the
* rtc interface left us a way to do that.
*/
struct hp_sdc_rtc_time alm_tm;
if (copy_from_user(&alm_tm, (struct hp_sdc_rtc_time*)arg,
sizeof(struct hp_sdc_rtc_time)))
return -EFAULT;
if (alm_tm.tm_hour > 23) return -EINVAL;
if (alm_tm.tm_min > 59) return -EINVAL;
if (alm_tm.tm_sec > 59) return -EINVAL;
ttime.sec = alm_tm.tm_hour * 3600 +
alm_tm.tm_min * 60 + alm_tm.tm_sec;
ttime.usec = 0;
if (hp_sdc_rtc_set_mt(&ttime)) return -EFAULT;
return 0;
}
case RTC_RD_TIME: /* Read the time/date from RTC */
{
if (hp_sdc_rtc_read_bbrtc(&wtime)) return -EFAULT;
break;
}
case RTC_SET_TIME: /* Set the RTC */
{
struct rtc_time hp_sdc_rtc_tm;
unsigned char mon, day, hrs, min, sec, leap_yr;
unsigned int yrs;
if (!capable(CAP_SYS_TIME))
return -EACCES;
if (copy_from_user(&hp_sdc_rtc_tm, (struct rtc_time *)arg,
sizeof(struct rtc_time)))
return -EFAULT;
yrs = hp_sdc_rtc_tm.tm_year + 1900;
mon = hp_sdc_rtc_tm.tm_mon + 1; /* tm_mon starts at zero */
day = hp_sdc_rtc_tm.tm_mday;
hrs = hp_sdc_rtc_tm.tm_hour;
min = hp_sdc_rtc_tm.tm_min;
sec = hp_sdc_rtc_tm.tm_sec;
if (yrs < 1970)
return -EINVAL;
leap_yr = ((!(yrs % 4) && (yrs % 100)) || !(yrs % 400));
if ((mon > 12) || (day == 0))
return -EINVAL;
if (day > (days_in_mo[mon] + ((mon == 2) && leap_yr)))
return -EINVAL;
if ((hrs >= 24) || (min >= 60) || (sec >= 60))
return -EINVAL;
if ((yrs -= eH) > 255) /* They are unsigned */
return -EINVAL;
return 0;
}
case RTC_EPOCH_READ: /* Read the epoch. */
{
return put_user (epoch, (unsigned long *)arg);
}
case RTC_EPOCH_SET: /* Set the epoch. */
{
/*
* There were no RTC clocks before 1900.
*/
if (arg < 1900)
return -EINVAL;
if (!capable(CAP_SYS_TIME))
return -EACCES;
epoch = arg;
return 0;
}
default:
return -EINVAL;
}
return copy_to_user((void *)arg, &wtime, sizeof wtime) ? -EFAULT : 0;
#endif
}
static const struct file_operations hp_sdc_rtc_fops = {
.owner = THIS_MODULE,
.llseek = no_llseek,
.read = hp_sdc_rtc_read,
.poll = hp_sdc_rtc_poll,
.ioctl = hp_sdc_rtc_ioctl,
.open = hp_sdc_rtc_open,
.fasync = hp_sdc_rtc_fasync,
};
static struct miscdevice hp_sdc_rtc_dev = {
.minor = RTC_MINOR,
.name = "rtc_HIL",
.fops = &hp_sdc_rtc_fops
};
static int __init hp_sdc_rtc_init(void)
{
int ret;
#ifdef __mc68000__
if (!MACH_IS_HP300)
return -ENODEV;
#endif
init_MUTEX(&i8042tregs);
if ((ret = hp_sdc_request_timer_irq(&hp_sdc_rtc_isr)))
return ret;
if (misc_register(&hp_sdc_rtc_dev) != 0)
printk(KERN_INFO "Could not register misc. dev for i8042 rtc\n");
create_proc_read_entry ("driver/rtc", 0, NULL,
hp_sdc_rtc_read_proc, NULL);
printk(KERN_INFO "HP i8042 SDC + MSM-58321 RTC support loaded "
"(RTC v " RTC_VERSION ")\n");
return 0;
}
static void __exit hp_sdc_rtc_exit(void)
{
remove_proc_entry ("driver/rtc", NULL);
misc_deregister(&hp_sdc_rtc_dev);
hp_sdc_release_timer_irq(hp_sdc_rtc_isr);
printk(KERN_INFO "HP i8042 SDC + MSM-58321 RTC support unloaded\n");
}
module_init(hp_sdc_rtc_init);
module_exit(hp_sdc_rtc_exit);

View File

@@ -0,0 +1,186 @@
/*
* Generic IXP4xx beeper driver
*
* Copyright (C) 2005 Tower Technologies
*
* based on nslu2-io.c
* Copyright (C) 2004 Karen Spearel
*
* Author: Alessandro Zummo <a.zummo@towertech.it>
* Maintainers: http://www.nslu2-linux.org/
*
* 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/module.h>
#include <linux/input.h>
#include <linux/delay.h>
#include <linux/platform_device.h>
#include <linux/interrupt.h>
#include <mach/hardware.h>
MODULE_AUTHOR("Alessandro Zummo <a.zummo@towertech.it>");
MODULE_DESCRIPTION("ixp4xx beeper driver");
MODULE_LICENSE("GPL");
MODULE_ALIAS("platform:ixp4xx-beeper");
static DEFINE_SPINLOCK(beep_lock);
static void ixp4xx_spkr_control(unsigned int pin, unsigned int count)
{
unsigned long flags;
spin_lock_irqsave(&beep_lock, flags);
if (count) {
gpio_line_config(pin, IXP4XX_GPIO_OUT);
gpio_line_set(pin, IXP4XX_GPIO_LOW);
*IXP4XX_OSRT2 = (count & ~IXP4XX_OST_RELOAD_MASK) | IXP4XX_OST_ENABLE;
} else {
gpio_line_config(pin, IXP4XX_GPIO_IN);
gpio_line_set(pin, IXP4XX_GPIO_HIGH);
*IXP4XX_OSRT2 = 0;
}
spin_unlock_irqrestore(&beep_lock, flags);
}
static int ixp4xx_spkr_event(struct input_dev *dev, unsigned int type, unsigned int code, int value)
{
unsigned int pin = (unsigned int) input_get_drvdata(dev);
unsigned int count = 0;
if (type != EV_SND)
return -1;
switch (code) {
case SND_BELL:
if (value)
value = 1000;
case SND_TONE:
break;
default:
return -1;
}
if (value > 20 && value < 32767)
#ifndef FREQ
count = (ixp4xx_get_board_tick_rate() / (value * 4)) - 1;
#else
count = (FREQ / (value * 4)) - 1;
#endif
ixp4xx_spkr_control(pin, count);
return 0;
}
static irqreturn_t ixp4xx_spkr_interrupt(int irq, void *dev_id)
{
/* clear interrupt */
*IXP4XX_OSST = IXP4XX_OSST_TIMER_2_PEND;
/* flip the beeper output */
*IXP4XX_GPIO_GPOUTR ^= (1 << (unsigned int) dev_id);
return IRQ_HANDLED;
}
static int __devinit ixp4xx_spkr_probe(struct platform_device *dev)
{
struct input_dev *input_dev;
int err;
input_dev = input_allocate_device();
if (!input_dev)
return -ENOMEM;
input_set_drvdata(input_dev, (void *) dev->id);
input_dev->name = "ixp4xx beeper",
input_dev->phys = "ixp4xx/gpio";
input_dev->id.bustype = BUS_HOST;
input_dev->id.vendor = 0x001f;
input_dev->id.product = 0x0001;
input_dev->id.version = 0x0100;
input_dev->dev.parent = &dev->dev;
input_dev->evbit[0] = BIT_MASK(EV_SND);
input_dev->sndbit[0] = BIT_MASK(SND_BELL) | BIT_MASK(SND_TONE);
input_dev->event = ixp4xx_spkr_event;
err = request_irq(IRQ_IXP4XX_TIMER2, &ixp4xx_spkr_interrupt,
IRQF_DISABLED | IRQF_TIMER, "ixp4xx-beeper", (void *) dev->id);
if (err)
goto err_free_device;
err = input_register_device(input_dev);
if (err)
goto err_free_irq;
platform_set_drvdata(dev, input_dev);
return 0;
err_free_irq:
free_irq(IRQ_IXP4XX_TIMER2, dev);
err_free_device:
input_free_device(input_dev);
return err;
}
static int __devexit ixp4xx_spkr_remove(struct platform_device *dev)
{
struct input_dev *input_dev = platform_get_drvdata(dev);
unsigned int pin = (unsigned int) input_get_drvdata(input_dev);
input_unregister_device(input_dev);
platform_set_drvdata(dev, NULL);
/* turn the speaker off */
disable_irq(IRQ_IXP4XX_TIMER2);
ixp4xx_spkr_control(pin, 0);
free_irq(IRQ_IXP4XX_TIMER2, dev);
return 0;
}
static void ixp4xx_spkr_shutdown(struct platform_device *dev)
{
struct input_dev *input_dev = platform_get_drvdata(dev);
unsigned int pin = (unsigned int) input_get_drvdata(input_dev);
/* turn off the speaker */
disable_irq(IRQ_IXP4XX_TIMER2);
ixp4xx_spkr_control(pin, 0);
}
static struct platform_driver ixp4xx_spkr_platform_driver = {
.driver = {
.name = "ixp4xx-beeper",
.owner = THIS_MODULE,
},
.probe = ixp4xx_spkr_probe,
.remove = __devexit_p(ixp4xx_spkr_remove),
.shutdown = ixp4xx_spkr_shutdown,
};
static int __init ixp4xx_spkr_init(void)
{
return platform_driver_register(&ixp4xx_spkr_platform_driver);
}
static void __exit ixp4xx_spkr_exit(void)
{
platform_driver_unregister(&ixp4xx_spkr_platform_driver);
}
module_init(ixp4xx_spkr_init);
module_exit(ixp4xx_spkr_exit);

View File

@@ -0,0 +1,607 @@
/*
* keyspan_remote: USB driver for the Keyspan DMR
*
* Copyright (C) 2005 Zymeta Corporation - Michael Downey (downey@zymeta.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, version 2.
*
* This driver has been put together with the support of Innosys, Inc.
* and Keyspan, Inc the manufacturers of the Keyspan USB DMR product.
*/
#include <linux/kernel.h>
#include <linux/errno.h>
#include <linux/init.h>
#include <linux/slab.h>
#include <linux/module.h>
#include <linux/usb/input.h>
#define DRIVER_VERSION "v0.1"
#define DRIVER_AUTHOR "Michael Downey <downey@zymeta.com>"
#define DRIVER_DESC "Driver for the USB Keyspan remote control."
#define DRIVER_LICENSE "GPL"
/* Parameters that can be passed to the driver. */
static int debug;
module_param(debug, int, 0444);
MODULE_PARM_DESC(debug, "Enable extra debug messages and information");
/* Vendor and product ids */
#define USB_KEYSPAN_VENDOR_ID 0x06CD
#define USB_KEYSPAN_PRODUCT_UIA11 0x0202
/* Defines for converting the data from the remote. */
#define ZERO 0x18
#define ZERO_MASK 0x1F /* 5 bits for a 0 */
#define ONE 0x3C
#define ONE_MASK 0x3F /* 6 bits for a 1 */
#define SYNC 0x3F80
#define SYNC_MASK 0x3FFF /* 14 bits for a SYNC sequence */
#define STOP 0x00
#define STOP_MASK 0x1F /* 5 bits for the STOP sequence */
#define GAP 0xFF
#define RECV_SIZE 8 /* The UIA-11 type have a 8 byte limit. */
/*
* Table that maps the 31 possible keycodes to input keys.
* Currently there are 15 and 17 button models so RESERVED codes
* are blank areas in the mapping.
*/
static const unsigned short keyspan_key_table[] = {
KEY_RESERVED, /* 0 is just a place holder. */
KEY_RESERVED,
KEY_STOP,
KEY_PLAYCD,
KEY_RESERVED,
KEY_PREVIOUSSONG,
KEY_REWIND,
KEY_FORWARD,
KEY_NEXTSONG,
KEY_RESERVED,
KEY_RESERVED,
KEY_RESERVED,
KEY_PAUSE,
KEY_VOLUMEUP,
KEY_RESERVED,
KEY_RESERVED,
KEY_RESERVED,
KEY_VOLUMEDOWN,
KEY_RESERVED,
KEY_UP,
KEY_RESERVED,
KEY_MUTE,
KEY_LEFT,
KEY_ENTER,
KEY_RIGHT,
KEY_RESERVED,
KEY_RESERVED,
KEY_DOWN,
KEY_RESERVED,
KEY_KPASTERISK,
KEY_RESERVED,
KEY_MENU
};
/* table of devices that work with this driver */
static struct usb_device_id keyspan_table[] = {
{ USB_DEVICE(USB_KEYSPAN_VENDOR_ID, USB_KEYSPAN_PRODUCT_UIA11) },
{ } /* Terminating entry */
};
/* Structure to store all the real stuff that a remote sends to us. */
struct keyspan_message {
u16 system;
u8 button;
u8 toggle;
};
/* Structure used for all the bit testing magic needed to be done. */
struct bit_tester {
u32 tester;
int len;
int pos;
int bits_left;
u8 buffer[32];
};
/* Structure to hold all of our driver specific stuff */
struct usb_keyspan {
char name[128];
char phys[64];
unsigned short keymap[ARRAY_SIZE(keyspan_key_table)];
struct usb_device *udev;
struct input_dev *input;
struct usb_interface *interface;
struct usb_endpoint_descriptor *in_endpoint;
struct urb* irq_urb;
int open;
dma_addr_t in_dma;
unsigned char *in_buffer;
/* variables used to parse messages from remote. */
struct bit_tester data;
int stage;
int toggle;
};
static struct usb_driver keyspan_driver;
/*
* Debug routine that prints out what we've received from the remote.
*/
static void keyspan_print(struct usb_keyspan* dev) /*unsigned char* data)*/
{
char codes[4 * RECV_SIZE];
int i;
for (i = 0; i < RECV_SIZE; i++)
snprintf(codes + i * 3, 4, "%02x ", dev->in_buffer[i]);
dev_info(&dev->udev->dev, "%s\n", codes);
}
/*
* Routine that manages the bit_tester structure. It makes sure that there are
* at least bits_needed bits loaded into the tester.
*/
static int keyspan_load_tester(struct usb_keyspan* dev, int bits_needed)
{
if (dev->data.bits_left >= bits_needed)
return 0;
/*
* Somehow we've missed the last message. The message will be repeated
* though so it's not too big a deal
*/
if (dev->data.pos >= dev->data.len) {
dev_dbg(&dev->udev->dev,
"%s - Error ran out of data. pos: %d, len: %d\n",
__func__, dev->data.pos, dev->data.len);
return -1;
}
/* Load as much as we can into the tester. */
while ((dev->data.bits_left + 7 < (sizeof(dev->data.tester) * 8)) &&
(dev->data.pos < dev->data.len)) {
dev->data.tester += (dev->data.buffer[dev->data.pos++] << dev->data.bits_left);
dev->data.bits_left += 8;
}
return 0;
}
static void keyspan_report_button(struct usb_keyspan *remote, int button, int press)
{
struct input_dev *input = remote->input;
input_event(input, EV_MSC, MSC_SCAN, button);
input_report_key(input, remote->keymap[button], press);
input_sync(input);
}
/*
* Routine that handles all the logic needed to parse out the message from the remote.
*/
static void keyspan_check_data(struct usb_keyspan *remote)
{
int i;
int found = 0;
struct keyspan_message message;
switch(remote->stage) {
case 0:
/*
* In stage 0 we want to find the start of a message. The remote sends a 0xFF as filler.
* So the first byte that isn't a FF should be the start of a new message.
*/
for (i = 0; i < RECV_SIZE && remote->in_buffer[i] == GAP; ++i);
if (i < RECV_SIZE) {
memcpy(remote->data.buffer, remote->in_buffer, RECV_SIZE);
remote->data.len = RECV_SIZE;
remote->data.pos = 0;
remote->data.tester = 0;
remote->data.bits_left = 0;
remote->stage = 1;
}
break;
case 1:
/*
* Stage 1 we should have 16 bytes and should be able to detect a
* SYNC. The SYNC is 14 bits, 7 0's and then 7 1's.
*/
memcpy(remote->data.buffer + remote->data.len, remote->in_buffer, RECV_SIZE);
remote->data.len += RECV_SIZE;
found = 0;
while ((remote->data.bits_left >= 14 || remote->data.pos < remote->data.len) && !found) {
for (i = 0; i < 8; ++i) {
if (keyspan_load_tester(remote, 14) != 0) {
remote->stage = 0;
return;
}
if ((remote->data.tester & SYNC_MASK) == SYNC) {
remote->data.tester = remote->data.tester >> 14;
remote->data.bits_left -= 14;
found = 1;
break;
} else {
remote->data.tester = remote->data.tester >> 1;
--remote->data.bits_left;
}
}
}
if (!found) {
remote->stage = 0;
remote->data.len = 0;
} else {
remote->stage = 2;
}
break;
case 2:
/*
* Stage 2 we should have 24 bytes which will be enough for a full
* message. We need to parse out the system code, button code,
* toggle code, and stop.
*/
memcpy(remote->data.buffer + remote->data.len, remote->in_buffer, RECV_SIZE);
remote->data.len += RECV_SIZE;
message.system = 0;
for (i = 0; i < 9; i++) {
keyspan_load_tester(remote, 6);
if ((remote->data.tester & ZERO_MASK) == ZERO) {
message.system = message.system << 1;
remote->data.tester = remote->data.tester >> 5;
remote->data.bits_left -= 5;
} else if ((remote->data.tester & ONE_MASK) == ONE) {
message.system = (message.system << 1) + 1;
remote->data.tester = remote->data.tester >> 6;
remote->data.bits_left -= 6;
} else {
err("%s - Unknown sequence found in system data.\n", __func__);
remote->stage = 0;
return;
}
}
message.button = 0;
for (i = 0; i < 5; i++) {
keyspan_load_tester(remote, 6);
if ((remote->data.tester & ZERO_MASK) == ZERO) {
message.button = message.button << 1;
remote->data.tester = remote->data.tester >> 5;
remote->data.bits_left -= 5;
} else if ((remote->data.tester & ONE_MASK) == ONE) {
message.button = (message.button << 1) + 1;
remote->data.tester = remote->data.tester >> 6;
remote->data.bits_left -= 6;
} else {
err("%s - Unknown sequence found in button data.\n", __func__);
remote->stage = 0;
return;
}
}
keyspan_load_tester(remote, 6);
if ((remote->data.tester & ZERO_MASK) == ZERO) {
message.toggle = 0;
remote->data.tester = remote->data.tester >> 5;
remote->data.bits_left -= 5;
} else if ((remote->data.tester & ONE_MASK) == ONE) {
message.toggle = 1;
remote->data.tester = remote->data.tester >> 6;
remote->data.bits_left -= 6;
} else {
err("%s - Error in message, invalid toggle.\n", __func__);
remote->stage = 0;
return;
}
keyspan_load_tester(remote, 5);
if ((remote->data.tester & STOP_MASK) == STOP) {
remote->data.tester = remote->data.tester >> 5;
remote->data.bits_left -= 5;
} else {
err("Bad message recieved, no stop bit found.\n");
}
dev_dbg(&remote->udev->dev,
"%s found valid message: system: %d, button: %d, toggle: %d\n",
__func__, message.system, message.button, message.toggle);
if (message.toggle != remote->toggle) {
keyspan_report_button(remote, message.button, 1);
keyspan_report_button(remote, message.button, 0);
remote->toggle = message.toggle;
}
remote->stage = 0;
break;
}
}
/*
* Routine for sending all the initialization messages to the remote.
*/
static int keyspan_setup(struct usb_device* dev)
{
int retval = 0;
retval = usb_control_msg(dev, usb_sndctrlpipe(dev, 0),
0x11, 0x40, 0x5601, 0x0, NULL, 0, 0);
if (retval) {
dev_dbg(&dev->dev, "%s - failed to set bit rate due to error: %d\n",
__func__, retval);
return(retval);
}
retval = usb_control_msg(dev, usb_sndctrlpipe(dev, 0),
0x44, 0x40, 0x0, 0x0, NULL, 0, 0);
if (retval) {
dev_dbg(&dev->dev, "%s - failed to set resume sensitivity due to error: %d\n",
__func__, retval);
return(retval);
}
retval = usb_control_msg(dev, usb_sndctrlpipe(dev, 0),
0x22, 0x40, 0x0, 0x0, NULL, 0, 0);
if (retval) {
dev_dbg(&dev->dev, "%s - failed to turn receive on due to error: %d\n",
__func__, retval);
return(retval);
}
dev_dbg(&dev->dev, "%s - Setup complete.\n", __func__);
return(retval);
}
/*
* Routine used to handle a new message that has come in.
*/
static void keyspan_irq_recv(struct urb *urb)
{
struct usb_keyspan *dev = urb->context;
int retval;
/* Check our status in case we need to bail out early. */
switch (urb->status) {
case 0:
break;
/* Device went away so don't keep trying to read from it. */
case -ECONNRESET:
case -ENOENT:
case -ESHUTDOWN:
return;
default:
goto resubmit;
break;
}
if (debug)
keyspan_print(dev);
keyspan_check_data(dev);
resubmit:
retval = usb_submit_urb(urb, GFP_ATOMIC);
if (retval)
err ("%s - usb_submit_urb failed with result: %d", __func__, retval);
}
static int keyspan_open(struct input_dev *dev)
{
struct usb_keyspan *remote = input_get_drvdata(dev);
remote->irq_urb->dev = remote->udev;
if (usb_submit_urb(remote->irq_urb, GFP_KERNEL))
return -EIO;
return 0;
}
static void keyspan_close(struct input_dev *dev)
{
struct usb_keyspan *remote = input_get_drvdata(dev);
usb_kill_urb(remote->irq_urb);
}
static struct usb_endpoint_descriptor *keyspan_get_in_endpoint(struct usb_host_interface *iface)
{
struct usb_endpoint_descriptor *endpoint;
int i;
for (i = 0; i < iface->desc.bNumEndpoints; ++i) {
endpoint = &iface->endpoint[i].desc;
if (usb_endpoint_is_int_in(endpoint)) {
/* we found our interrupt in endpoint */
return endpoint;
}
}
return NULL;
}
/*
* Routine that sets up the driver to handle a specific USB device detected on the bus.
*/
static int keyspan_probe(struct usb_interface *interface, const struct usb_device_id *id)
{
struct usb_device *udev = interface_to_usbdev(interface);
struct usb_endpoint_descriptor *endpoint;
struct usb_keyspan *remote;
struct input_dev *input_dev;
int i, error;
endpoint = keyspan_get_in_endpoint(interface->cur_altsetting);
if (!endpoint)
return -ENODEV;
remote = kzalloc(sizeof(*remote), GFP_KERNEL);
input_dev = input_allocate_device();
if (!remote || !input_dev) {
error = -ENOMEM;
goto fail1;
}
remote->udev = udev;
remote->input = input_dev;
remote->interface = interface;
remote->in_endpoint = endpoint;
remote->toggle = -1; /* Set to -1 so we will always not match the toggle from the first remote message. */
remote->in_buffer = usb_buffer_alloc(udev, RECV_SIZE, GFP_ATOMIC, &remote->in_dma);
if (!remote->in_buffer) {
error = -ENOMEM;
goto fail1;
}
remote->irq_urb = usb_alloc_urb(0, GFP_KERNEL);
if (!remote->irq_urb) {
error = -ENOMEM;
goto fail2;
}
error = keyspan_setup(udev);
if (error) {
error = -ENODEV;
goto fail3;
}
if (udev->manufacturer)
strlcpy(remote->name, udev->manufacturer, sizeof(remote->name));
if (udev->product) {
if (udev->manufacturer)
strlcat(remote->name, " ", sizeof(remote->name));
strlcat(remote->name, udev->product, sizeof(remote->name));
}
if (!strlen(remote->name))
snprintf(remote->name, sizeof(remote->name),
"USB Keyspan Remote %04x:%04x",
le16_to_cpu(udev->descriptor.idVendor),
le16_to_cpu(udev->descriptor.idProduct));
usb_make_path(udev, remote->phys, sizeof(remote->phys));
strlcat(remote->phys, "/input0", sizeof(remote->phys));
memcpy(remote->keymap, keyspan_key_table, sizeof(remote->keymap));
input_dev->name = remote->name;
input_dev->phys = remote->phys;
usb_to_input_id(udev, &input_dev->id);
input_dev->dev.parent = &interface->dev;
input_dev->keycode = remote->keymap;
input_dev->keycodesize = sizeof(unsigned short);
input_dev->keycodemax = ARRAY_SIZE(remote->keymap);
input_set_capability(input_dev, EV_MSC, MSC_SCAN);
__set_bit(EV_KEY, input_dev->evbit);
for (i = 0; i < ARRAY_SIZE(keyspan_key_table); i++)
__set_bit(keyspan_key_table[i], input_dev->keybit);
__clear_bit(KEY_RESERVED, input_dev->keybit);
input_set_drvdata(input_dev, remote);
input_dev->open = keyspan_open;
input_dev->close = keyspan_close;
/*
* Initialize the URB to access the device.
* The urb gets sent to the device in keyspan_open()
*/
usb_fill_int_urb(remote->irq_urb,
remote->udev,
usb_rcvintpipe(remote->udev, endpoint->bEndpointAddress),
remote->in_buffer, RECV_SIZE, keyspan_irq_recv, remote,
endpoint->bInterval);
remote->irq_urb->transfer_dma = remote->in_dma;
remote->irq_urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;
/* we can register the device now, as it is ready */
error = input_register_device(remote->input);
if (error)
goto fail3;
/* save our data pointer in this interface device */
usb_set_intfdata(interface, remote);
return 0;
fail3: usb_free_urb(remote->irq_urb);
fail2: usb_buffer_free(udev, RECV_SIZE, remote->in_buffer, remote->in_dma);
fail1: kfree(remote);
input_free_device(input_dev);
return error;
}
/*
* Routine called when a device is disconnected from the USB.
*/
static void keyspan_disconnect(struct usb_interface *interface)
{
struct usb_keyspan *remote;
remote = usb_get_intfdata(interface);
usb_set_intfdata(interface, NULL);
if (remote) { /* We have a valid driver structure so clean up everything we allocated. */
input_unregister_device(remote->input);
usb_kill_urb(remote->irq_urb);
usb_free_urb(remote->irq_urb);
usb_buffer_free(remote->udev, RECV_SIZE, remote->in_buffer, remote->in_dma);
kfree(remote);
}
}
/*
* Standard driver set up sections
*/
static struct usb_driver keyspan_driver =
{
.name = "keyspan_remote",
.probe = keyspan_probe,
.disconnect = keyspan_disconnect,
.id_table = keyspan_table
};
static int __init usb_keyspan_init(void)
{
int result;
/* register this driver with the USB subsystem */
result = usb_register(&keyspan_driver);
if (result)
err("usb_register failed. Error number %d\n", result);
return result;
}
static void __exit usb_keyspan_exit(void)
{
/* deregister this driver with the USB subsystem */
usb_deregister(&keyspan_driver);
}
module_init(usb_keyspan_init);
module_exit(usb_keyspan_exit);
MODULE_DEVICE_TABLE(usb, keyspan_table);
MODULE_AUTHOR(DRIVER_AUTHOR);
MODULE_DESCRIPTION(DRIVER_DESC);
MODULE_LICENSE(DRIVER_LICENSE);

View File

@@ -0,0 +1,151 @@
/*
* m68k beeper driver for Linux
*
* Copyright (c) 2002 Richard Zidlicky
* Copyright (c) 2002 Vojtech Pavlik
* Copyright (c) 1992 Orest Zborowski
*
*/
/*
* 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/module.h>
#include <linux/init.h>
#include <linux/input.h>
#include <linux/platform_device.h>
#include <asm/machdep.h>
#include <asm/io.h>
MODULE_AUTHOR("Richard Zidlicky <rz@linux-m68k.org>");
MODULE_DESCRIPTION("m68k beeper driver");
MODULE_LICENSE("GPL");
static struct platform_device *m68kspkr_platform_device;
static int m68kspkr_event(struct input_dev *dev, unsigned int type, unsigned int code, int value)
{
unsigned int count = 0;
if (type != EV_SND)
return -1;
switch (code) {
case SND_BELL: if (value) value = 1000;
case SND_TONE: break;
default: return -1;
}
if (value > 20 && value < 32767)
count = 1193182 / value;
mach_beep(count, -1);
return 0;
}
static int __devinit m68kspkr_probe(struct platform_device *dev)
{
struct input_dev *input_dev;
int err;
input_dev = input_allocate_device();
if (!input_dev)
return -ENOMEM;
input_dev->name = "m68k beeper";
input_dev->phys = "m68k/generic";
input_dev->id.bustype = BUS_HOST;
input_dev->id.vendor = 0x001f;
input_dev->id.product = 0x0001;
input_dev->id.version = 0x0100;
input_dev->dev.parent = &dev->dev;
input_dev->evbit[0] = BIT_MASK(EV_SND);
input_dev->sndbit[0] = BIT_MASK(SND_BELL) | BIT_MASK(SND_TONE);
input_dev->event = m68kspkr_event;
err = input_register_device(input_dev);
if (err) {
input_free_device(input_dev);
return err;
}
platform_set_drvdata(dev, input_dev);
return 0;
}
static int __devexit m68kspkr_remove(struct platform_device *dev)
{
struct input_dev *input_dev = platform_get_drvdata(dev);
input_unregister_device(input_dev);
platform_set_drvdata(dev, NULL);
/* turn off the speaker */
m68kspkr_event(NULL, EV_SND, SND_BELL, 0);
return 0;
}
static void m68kspkr_shutdown(struct platform_device *dev)
{
/* turn off the speaker */
m68kspkr_event(NULL, EV_SND, SND_BELL, 0);
}
static struct platform_driver m68kspkr_platform_driver = {
.driver = {
.name = "m68kspkr",
.owner = THIS_MODULE,
},
.probe = m68kspkr_probe,
.remove = __devexit_p(m68kspkr_remove),
.shutdown = m68kspkr_shutdown,
};
static int __init m68kspkr_init(void)
{
int err;
if (!mach_beep) {
printk(KERN_INFO "m68kspkr: no lowlevel beep support\n");
return -ENODEV;
}
err = platform_driver_register(&m68kspkr_platform_driver);
if (err)
return err;
m68kspkr_platform_device = platform_device_alloc("m68kspkr", -1);
if (!m68kspkr_platform_device) {
err = -ENOMEM;
goto err_unregister_driver;
}
err = platform_device_add(m68kspkr_platform_device);
if (err)
goto err_free_device;
return 0;
err_free_device:
platform_device_put(m68kspkr_platform_device);
err_unregister_driver:
platform_driver_unregister(&m68kspkr_platform_driver);
return err;
}
static void __exit m68kspkr_exit(void)
{
platform_device_unregister(m68kspkr_platform_device);
platform_driver_unregister(&m68kspkr_platform_driver);
}
module_init(m68kspkr_init);
module_exit(m68kspkr_exit);

View File

@@ -0,0 +1,144 @@
/*
* Input driver for PCAP events:
* * Power key
* * Headphone button
*
* Copyright (c) 2008,2009 Ilya Petrov <ilya.muromec@gmail.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*
*/
#include <linux/module.h>
#include <linux/init.h>
#include <linux/interrupt.h>
#include <linux/platform_device.h>
#include <linux/input.h>
#include <linux/mfd/ezx-pcap.h>
struct pcap_keys {
struct pcap_chip *pcap;
struct input_dev *input;
};
/* PCAP2 interrupts us on keypress */
static irqreturn_t pcap_keys_handler(int irq, void *_pcap_keys)
{
struct pcap_keys *pcap_keys = _pcap_keys;
int pirq = irq_to_pcap(pcap_keys->pcap, irq);
u32 pstat;
ezx_pcap_read(pcap_keys->pcap, PCAP_REG_PSTAT, &pstat);
pstat &= 1 << pirq;
switch (pirq) {
case PCAP_IRQ_ONOFF:
input_report_key(pcap_keys->input, KEY_POWER, !pstat);
break;
case PCAP_IRQ_MIC:
input_report_key(pcap_keys->input, KEY_HP, !pstat);
break;
}
input_sync(pcap_keys->input);
return IRQ_HANDLED;
}
static int __devinit pcap_keys_probe(struct platform_device *pdev)
{
int err = -ENOMEM;
struct pcap_keys *pcap_keys;
struct input_dev *input_dev;
pcap_keys = kmalloc(sizeof(struct pcap_keys), GFP_KERNEL);
if (!pcap_keys)
return err;
pcap_keys->pcap = dev_get_drvdata(pdev->dev.parent);
input_dev = input_allocate_device();
if (!input_dev)
goto fail;
pcap_keys->input = input_dev;
platform_set_drvdata(pdev, pcap_keys);
input_dev->name = pdev->name;
input_dev->phys = "pcap-keys/input0";
input_dev->id.bustype = BUS_HOST;
input_dev->dev.parent = &pdev->dev;
__set_bit(EV_KEY, input_dev->evbit);
__set_bit(KEY_POWER, input_dev->keybit);
__set_bit(KEY_HP, input_dev->keybit);
err = input_register_device(input_dev);
if (err)
goto fail_allocate;
err = request_irq(pcap_to_irq(pcap_keys->pcap, PCAP_IRQ_ONOFF),
pcap_keys_handler, 0, "Power key", pcap_keys);
if (err)
goto fail_register;
err = request_irq(pcap_to_irq(pcap_keys->pcap, PCAP_IRQ_MIC),
pcap_keys_handler, 0, "Headphone button", pcap_keys);
if (err)
goto fail_pwrkey;
return 0;
fail_pwrkey:
free_irq(pcap_to_irq(pcap_keys->pcap, PCAP_IRQ_ONOFF), pcap_keys);
fail_register:
input_unregister_device(input_dev);
goto fail;
fail_allocate:
input_free_device(input_dev);
fail:
kfree(pcap_keys);
return err;
}
static int __devexit pcap_keys_remove(struct platform_device *pdev)
{
struct pcap_keys *pcap_keys = platform_get_drvdata(pdev);
free_irq(pcap_to_irq(pcap_keys->pcap, PCAP_IRQ_ONOFF), pcap_keys);
free_irq(pcap_to_irq(pcap_keys->pcap, PCAP_IRQ_MIC), pcap_keys);
input_unregister_device(pcap_keys->input);
kfree(pcap_keys);
return 0;
}
static struct platform_driver pcap_keys_device_driver = {
.probe = pcap_keys_probe,
.remove = __devexit_p(pcap_keys_remove),
.driver = {
.name = "pcap-keys",
.owner = THIS_MODULE,
}
};
static int __init pcap_keys_init(void)
{
return platform_driver_register(&pcap_keys_device_driver);
};
static void __exit pcap_keys_exit(void)
{
platform_driver_unregister(&pcap_keys_device_driver);
};
module_init(pcap_keys_init);
module_exit(pcap_keys_exit);
MODULE_DESCRIPTION("Motorola PCAP2 input events driver");
MODULE_AUTHOR("Ilya Petrov <ilya.muromec@gmail.com>");
MODULE_LICENSE("GPL");
MODULE_ALIAS("platform:pcap_keys");

View File

@@ -0,0 +1,132 @@
/* NXP PCF50633 Input Driver
*
* (C) 2006-2008 by Openmoko, Inc.
* Author: Balaji Rao <balajirrao@openmoko.org>
* All rights reserved.
*
* Broken down from monstrous PCF50633 driver mainly by
* Harald Welte, Andy Green and Werner Almesberger
*
* 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/device.h>
#include <linux/platform_device.h>
#include <linux/input.h>
#include <linux/mfd/pcf50633/core.h>
#define PCF50633_OOCSTAT_ONKEY 0x01
#define PCF50633_REG_OOCSTAT 0x12
#define PCF50633_REG_OOCMODE 0x10
struct pcf50633_input {
struct pcf50633 *pcf;
struct input_dev *input_dev;
};
static void
pcf50633_input_irq(int irq, void *data)
{
struct pcf50633_input *input;
int onkey_released;
input = data;
/* We report only one event depending on the key press status */
onkey_released = pcf50633_reg_read(input->pcf, PCF50633_REG_OOCSTAT)
& PCF50633_OOCSTAT_ONKEY;
if (irq == PCF50633_IRQ_ONKEYF && !onkey_released)
input_report_key(input->input_dev, KEY_POWER, 1);
else if (irq == PCF50633_IRQ_ONKEYR && onkey_released)
input_report_key(input->input_dev, KEY_POWER, 0);
input_sync(input->input_dev);
}
static int __devinit pcf50633_input_probe(struct platform_device *pdev)
{
struct pcf50633_input *input;
struct pcf50633_subdev_pdata *pdata = pdev->dev.platform_data;
struct input_dev *input_dev;
int ret;
input = kzalloc(sizeof(*input), GFP_KERNEL);
if (!input)
return -ENOMEM;
input_dev = input_allocate_device();
if (!input_dev) {
kfree(input);
return -ENOMEM;
}
platform_set_drvdata(pdev, input);
input->pcf = pdata->pcf;
input->input_dev = input_dev;
input_dev->name = "PCF50633 PMU events";
input_dev->id.bustype = BUS_I2C;
input_dev->evbit[0] = BIT(EV_KEY) | BIT(EV_PWR);
set_bit(KEY_POWER, input_dev->keybit);
ret = input_register_device(input_dev);
if (ret) {
input_free_device(input_dev);
kfree(input);
return ret;
}
pcf50633_register_irq(pdata->pcf, PCF50633_IRQ_ONKEYR,
pcf50633_input_irq, input);
pcf50633_register_irq(pdata->pcf, PCF50633_IRQ_ONKEYF,
pcf50633_input_irq, input);
return 0;
}
static int __devexit pcf50633_input_remove(struct platform_device *pdev)
{
struct pcf50633_input *input = platform_get_drvdata(pdev);
pcf50633_free_irq(input->pcf, PCF50633_IRQ_ONKEYR);
pcf50633_free_irq(input->pcf, PCF50633_IRQ_ONKEYF);
input_unregister_device(input->input_dev);
kfree(input);
return 0;
}
static struct platform_driver pcf50633_input_driver = {
.driver = {
.name = "pcf50633-input",
},
.probe = pcf50633_input_probe,
.remove = __devexit_p(pcf50633_input_remove),
};
static int __init pcf50633_input_init(void)
{
return platform_driver_register(&pcf50633_input_driver);
}
module_init(pcf50633_input_init);
static void __exit pcf50633_input_exit(void)
{
platform_driver_unregister(&pcf50633_input_driver);
}
module_exit(pcf50633_input_exit);
MODULE_AUTHOR("Balaji Rao <balajirrao@openmoko.org>");
MODULE_DESCRIPTION("PCF50633 input driver");
MODULE_LICENSE("GPL");
MODULE_ALIAS("platform:pcf50633-input");

View File

@@ -0,0 +1,157 @@
/*
* PC Speaker beeper driver for Linux
*
* Copyright (c) 2002 Vojtech Pavlik
* Copyright (c) 1992 Orest Zborowski
*
*/
/*
* 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/module.h>
#include <linux/init.h>
#include <linux/input.h>
#include <linux/platform_device.h>
#include <linux/timex.h>
#include <asm/io.h>
MODULE_AUTHOR("Vojtech Pavlik <vojtech@ucw.cz>");
MODULE_DESCRIPTION("PC Speaker beeper driver");
MODULE_LICENSE("GPL");
MODULE_ALIAS("platform:pcspkr");
#if defined(CONFIG_MIPS) || defined(CONFIG_X86)
/* Use the global PIT lock ! */
#include <asm/i8253.h>
#else
#include <asm/8253pit.h>
static DEFINE_SPINLOCK(i8253_lock);
#endif
static int pcspkr_event(struct input_dev *dev, unsigned int type, unsigned int code, int value)
{
unsigned int count = 0;
unsigned long flags;
if (type != EV_SND)
return -1;
switch (code) {
case SND_BELL: if (value) value = 1000;
case SND_TONE: break;
default: return -1;
}
if (value > 20 && value < 32767)
count = PIT_TICK_RATE / value;
spin_lock_irqsave(&i8253_lock, flags);
if (count) {
/* set command for counter 2, 2 byte write */
outb_p(0xB6, 0x43);
/* select desired HZ */
outb_p(count & 0xff, 0x42);
outb((count >> 8) & 0xff, 0x42);
/* enable counter 2 */
outb_p(inb_p(0x61) | 3, 0x61);
} else {
/* disable counter 2 */
outb(inb_p(0x61) & 0xFC, 0x61);
}
spin_unlock_irqrestore(&i8253_lock, flags);
return 0;
}
static int __devinit pcspkr_probe(struct platform_device *dev)
{
struct input_dev *pcspkr_dev;
int err;
pcspkr_dev = input_allocate_device();
if (!pcspkr_dev)
return -ENOMEM;
pcspkr_dev->name = "PC Speaker";
pcspkr_dev->phys = "isa0061/input0";
pcspkr_dev->id.bustype = BUS_ISA;
pcspkr_dev->id.vendor = 0x001f;
pcspkr_dev->id.product = 0x0001;
pcspkr_dev->id.version = 0x0100;
pcspkr_dev->dev.parent = &dev->dev;
pcspkr_dev->evbit[0] = BIT_MASK(EV_SND);
pcspkr_dev->sndbit[0] = BIT_MASK(SND_BELL) | BIT_MASK(SND_TONE);
pcspkr_dev->event = pcspkr_event;
err = input_register_device(pcspkr_dev);
if (err) {
input_free_device(pcspkr_dev);
return err;
}
platform_set_drvdata(dev, pcspkr_dev);
return 0;
}
static int __devexit pcspkr_remove(struct platform_device *dev)
{
struct input_dev *pcspkr_dev = platform_get_drvdata(dev);
input_unregister_device(pcspkr_dev);
platform_set_drvdata(dev, NULL);
/* turn off the speaker */
pcspkr_event(NULL, EV_SND, SND_BELL, 0);
return 0;
}
static int pcspkr_suspend(struct device *dev)
{
pcspkr_event(NULL, EV_SND, SND_BELL, 0);
return 0;
}
static void pcspkr_shutdown(struct platform_device *dev)
{
/* turn off the speaker */
pcspkr_event(NULL, EV_SND, SND_BELL, 0);
}
static struct dev_pm_ops pcspkr_pm_ops = {
.suspend = pcspkr_suspend,
};
static struct platform_driver pcspkr_platform_driver = {
.driver = {
.name = "pcspkr",
.owner = THIS_MODULE,
.pm = &pcspkr_pm_ops,
},
.probe = pcspkr_probe,
.remove = __devexit_p(pcspkr_remove),
.shutdown = pcspkr_shutdown,
};
static int __init pcspkr_init(void)
{
return platform_driver_register(&pcspkr_platform_driver);
}
static void __exit pcspkr_exit(void)
{
platform_driver_unregister(&pcspkr_platform_driver);
}
module_init(pcspkr_init);
module_exit(pcspkr_exit);

View File

@@ -0,0 +1,464 @@
/*
* A driver for the Griffin Technology, Inc. "PowerMate" USB controller dial.
*
* v1.1, (c)2002 William R Sowerbutts <will@sowerbutts.com>
*
* This device is a anodised aluminium knob which connects over USB. It can measure
* clockwise and anticlockwise rotation. The dial also acts as a pushbutton with
* a spring for automatic release. The base contains a pair of LEDs which illuminate
* the translucent base. It rotates without limit and reports its relative rotation
* back to the host when polled by the USB controller.
*
* Testing with the knob I have has shown that it measures approximately 94 "clicks"
* for one full rotation. Testing with my High Speed Rotation Actuator (ok, it was
* a variable speed cordless electric drill) has shown that the device can measure
* speeds of up to 7 clicks either clockwise or anticlockwise between pollings from
* the host. If it counts more than 7 clicks before it is polled, it will wrap back
* to zero and start counting again. This was at quite high speed, however, almost
* certainly faster than the human hand could turn it. Griffin say that it loses a
* pulse or two on a direction change; the granularity is so fine that I never
* noticed this in practice.
*
* The device's microcontroller can be programmed to set the LED to either a constant
* intensity, or to a rhythmic pulsing. Several patterns and speeds are available.
*
* Griffin were very happy to provide documentation and free hardware for development.
*
* Some userspace tools are available on the web: http://sowerbutts.com/powermate/
*
*/
#include <linux/kernel.h>
#include <linux/slab.h>
#include <linux/module.h>
#include <linux/init.h>
#include <linux/spinlock.h>
#include <linux/usb/input.h>
#define POWERMATE_VENDOR 0x077d /* Griffin Technology, Inc. */
#define POWERMATE_PRODUCT_NEW 0x0410 /* Griffin PowerMate */
#define POWERMATE_PRODUCT_OLD 0x04AA /* Griffin soundKnob */
#define CONTOUR_VENDOR 0x05f3 /* Contour Design, Inc. */
#define CONTOUR_JOG 0x0240 /* Jog and Shuttle */
/* these are the command codes we send to the device */
#define SET_STATIC_BRIGHTNESS 0x01
#define SET_PULSE_ASLEEP 0x02
#define SET_PULSE_AWAKE 0x03
#define SET_PULSE_MODE 0x04
/* these refer to bits in the powermate_device's requires_update field. */
#define UPDATE_STATIC_BRIGHTNESS (1<<0)
#define UPDATE_PULSE_ASLEEP (1<<1)
#define UPDATE_PULSE_AWAKE (1<<2)
#define UPDATE_PULSE_MODE (1<<3)
/* at least two versions of the hardware exist, with differing payload
sizes. the first three bytes always contain the "interesting" data in
the relevant format. */
#define POWERMATE_PAYLOAD_SIZE_MAX 6
#define POWERMATE_PAYLOAD_SIZE_MIN 3
struct powermate_device {
signed char *data;
dma_addr_t data_dma;
struct urb *irq, *config;
struct usb_ctrlrequest *configcr;
dma_addr_t configcr_dma;
struct usb_device *udev;
struct input_dev *input;
spinlock_t lock;
int static_brightness;
int pulse_speed;
int pulse_table;
int pulse_asleep;
int pulse_awake;
int requires_update; // physical settings which are out of sync
char phys[64];
};
static char pm_name_powermate[] = "Griffin PowerMate";
static char pm_name_soundknob[] = "Griffin SoundKnob";
static void powermate_config_complete(struct urb *urb);
/* Callback for data arriving from the PowerMate over the USB interrupt pipe */
static void powermate_irq(struct urb *urb)
{
struct powermate_device *pm = urb->context;
int retval;
switch (urb->status) {
case 0:
/* success */
break;
case -ECONNRESET:
case -ENOENT:
case -ESHUTDOWN:
/* this urb is terminated, clean up */
dbg("%s - urb shutting down with status: %d", __func__, urb->status);
return;
default:
dbg("%s - nonzero urb status received: %d", __func__, urb->status);
goto exit;
}
/* handle updates to device state */
input_report_key(pm->input, BTN_0, pm->data[0] & 0x01);
input_report_rel(pm->input, REL_DIAL, pm->data[1]);
input_sync(pm->input);
exit:
retval = usb_submit_urb (urb, GFP_ATOMIC);
if (retval)
err ("%s - usb_submit_urb failed with result %d",
__func__, retval);
}
/* Decide if we need to issue a control message and do so. Must be called with pm->lock taken */
static void powermate_sync_state(struct powermate_device *pm)
{
if (pm->requires_update == 0)
return; /* no updates are required */
if (pm->config->status == -EINPROGRESS)
return; /* an update is already in progress; it'll issue this update when it completes */
if (pm->requires_update & UPDATE_PULSE_ASLEEP){
pm->configcr->wValue = cpu_to_le16( SET_PULSE_ASLEEP );
pm->configcr->wIndex = cpu_to_le16( pm->pulse_asleep ? 1 : 0 );
pm->requires_update &= ~UPDATE_PULSE_ASLEEP;
}else if (pm->requires_update & UPDATE_PULSE_AWAKE){
pm->configcr->wValue = cpu_to_le16( SET_PULSE_AWAKE );
pm->configcr->wIndex = cpu_to_le16( pm->pulse_awake ? 1 : 0 );
pm->requires_update &= ~UPDATE_PULSE_AWAKE;
}else if (pm->requires_update & UPDATE_PULSE_MODE){
int op, arg;
/* the powermate takes an operation and an argument for its pulse algorithm.
the operation can be:
0: divide the speed
1: pulse at normal speed
2: multiply the speed
the argument only has an effect for operations 0 and 2, and ranges between
1 (least effect) to 255 (maximum effect).
thus, several states are equivalent and are coalesced into one state.
we map this onto a range from 0 to 510, with:
0 -- 254 -- use divide (0 = slowest)
255 -- use normal speed
256 -- 510 -- use multiple (510 = fastest).
Only values of 'arg' quite close to 255 are particularly useful/spectacular.
*/
if (pm->pulse_speed < 255) {
op = 0; // divide
arg = 255 - pm->pulse_speed;
} else if (pm->pulse_speed > 255) {
op = 2; // multiply
arg = pm->pulse_speed - 255;
} else {
op = 1; // normal speed
arg = 0; // can be any value
}
pm->configcr->wValue = cpu_to_le16( (pm->pulse_table << 8) | SET_PULSE_MODE );
pm->configcr->wIndex = cpu_to_le16( (arg << 8) | op );
pm->requires_update &= ~UPDATE_PULSE_MODE;
} else if (pm->requires_update & UPDATE_STATIC_BRIGHTNESS) {
pm->configcr->wValue = cpu_to_le16( SET_STATIC_BRIGHTNESS );
pm->configcr->wIndex = cpu_to_le16( pm->static_brightness );
pm->requires_update &= ~UPDATE_STATIC_BRIGHTNESS;
} else {
printk(KERN_ERR "powermate: unknown update required");
pm->requires_update = 0; /* fudge the bug */
return;
}
/* printk("powermate: %04x %04x\n", pm->configcr->wValue, pm->configcr->wIndex); */
pm->configcr->bRequestType = 0x41; /* vendor request */
pm->configcr->bRequest = 0x01;
pm->configcr->wLength = 0;
usb_fill_control_urb(pm->config, pm->udev, usb_sndctrlpipe(pm->udev, 0),
(void *) pm->configcr, NULL, 0,
powermate_config_complete, pm);
pm->config->setup_dma = pm->configcr_dma;
pm->config->transfer_flags |= URB_NO_SETUP_DMA_MAP;
if (usb_submit_urb(pm->config, GFP_ATOMIC))
printk(KERN_ERR "powermate: usb_submit_urb(config) failed");
}
/* Called when our asynchronous control message completes. We may need to issue another immediately */
static void powermate_config_complete(struct urb *urb)
{
struct powermate_device *pm = urb->context;
unsigned long flags;
if (urb->status)
printk(KERN_ERR "powermate: config urb returned %d\n", urb->status);
spin_lock_irqsave(&pm->lock, flags);
powermate_sync_state(pm);
spin_unlock_irqrestore(&pm->lock, flags);
}
/* Set the LED up as described and begin the sync with the hardware if required */
static void powermate_pulse_led(struct powermate_device *pm, int static_brightness, int pulse_speed,
int pulse_table, int pulse_asleep, int pulse_awake)
{
unsigned long flags;
if (pulse_speed < 0)
pulse_speed = 0;
if (pulse_table < 0)
pulse_table = 0;
if (pulse_speed > 510)
pulse_speed = 510;
if (pulse_table > 2)
pulse_table = 2;
pulse_asleep = !!pulse_asleep;
pulse_awake = !!pulse_awake;
spin_lock_irqsave(&pm->lock, flags);
/* mark state updates which are required */
if (static_brightness != pm->static_brightness) {
pm->static_brightness = static_brightness;
pm->requires_update |= UPDATE_STATIC_BRIGHTNESS;
}
if (pulse_asleep != pm->pulse_asleep) {
pm->pulse_asleep = pulse_asleep;
pm->requires_update |= (UPDATE_PULSE_ASLEEP | UPDATE_STATIC_BRIGHTNESS);
}
if (pulse_awake != pm->pulse_awake) {
pm->pulse_awake = pulse_awake;
pm->requires_update |= (UPDATE_PULSE_AWAKE | UPDATE_STATIC_BRIGHTNESS);
}
if (pulse_speed != pm->pulse_speed || pulse_table != pm->pulse_table) {
pm->pulse_speed = pulse_speed;
pm->pulse_table = pulse_table;
pm->requires_update |= UPDATE_PULSE_MODE;
}
powermate_sync_state(pm);
spin_unlock_irqrestore(&pm->lock, flags);
}
/* Callback from the Input layer when an event arrives from userspace to configure the LED */
static int powermate_input_event(struct input_dev *dev, unsigned int type, unsigned int code, int _value)
{
unsigned int command = (unsigned int)_value;
struct powermate_device *pm = input_get_drvdata(dev);
if (type == EV_MSC && code == MSC_PULSELED){
/*
bits 0- 7: 8 bits: LED brightness
bits 8-16: 9 bits: pulsing speed modifier (0 ... 510); 0-254 = slower, 255 = standard, 256-510 = faster.
bits 17-18: 2 bits: pulse table (0, 1, 2 valid)
bit 19: 1 bit : pulse whilst asleep?
bit 20: 1 bit : pulse constantly?
*/
int static_brightness = command & 0xFF; // bits 0-7
int pulse_speed = (command >> 8) & 0x1FF; // bits 8-16
int pulse_table = (command >> 17) & 0x3; // bits 17-18
int pulse_asleep = (command >> 19) & 0x1; // bit 19
int pulse_awake = (command >> 20) & 0x1; // bit 20
powermate_pulse_led(pm, static_brightness, pulse_speed, pulse_table, pulse_asleep, pulse_awake);
}
return 0;
}
static int powermate_alloc_buffers(struct usb_device *udev, struct powermate_device *pm)
{
pm->data = usb_buffer_alloc(udev, POWERMATE_PAYLOAD_SIZE_MAX,
GFP_ATOMIC, &pm->data_dma);
if (!pm->data)
return -1;
pm->configcr = usb_buffer_alloc(udev, sizeof(*(pm->configcr)),
GFP_ATOMIC, &pm->configcr_dma);
if (!pm->configcr)
return -1;
return 0;
}
static void powermate_free_buffers(struct usb_device *udev, struct powermate_device *pm)
{
usb_buffer_free(udev, POWERMATE_PAYLOAD_SIZE_MAX,
pm->data, pm->data_dma);
usb_buffer_free(udev, sizeof(*(pm->configcr)),
pm->configcr, pm->configcr_dma);
}
/* Called whenever a USB device matching one in our supported devices table is connected */
static int powermate_probe(struct usb_interface *intf, const struct usb_device_id *id)
{
struct usb_device *udev = interface_to_usbdev (intf);
struct usb_host_interface *interface;
struct usb_endpoint_descriptor *endpoint;
struct powermate_device *pm;
struct input_dev *input_dev;
int pipe, maxp;
int error = -ENOMEM;
interface = intf->cur_altsetting;
endpoint = &interface->endpoint[0].desc;
if (!usb_endpoint_is_int_in(endpoint))
return -EIO;
usb_control_msg(udev, usb_sndctrlpipe(udev, 0),
0x0a, USB_TYPE_CLASS | USB_RECIP_INTERFACE,
0, interface->desc.bInterfaceNumber, NULL, 0,
USB_CTRL_SET_TIMEOUT);
pm = kzalloc(sizeof(struct powermate_device), GFP_KERNEL);
input_dev = input_allocate_device();
if (!pm || !input_dev)
goto fail1;
if (powermate_alloc_buffers(udev, pm))
goto fail2;
pm->irq = usb_alloc_urb(0, GFP_KERNEL);
if (!pm->irq)
goto fail2;
pm->config = usb_alloc_urb(0, GFP_KERNEL);
if (!pm->config)
goto fail3;
pm->udev = udev;
pm->input = input_dev;
usb_make_path(udev, pm->phys, sizeof(pm->phys));
strlcpy(pm->phys, "/input0", sizeof(pm->phys));
spin_lock_init(&pm->lock);
switch (le16_to_cpu(udev->descriptor.idProduct)) {
case POWERMATE_PRODUCT_NEW:
input_dev->name = pm_name_powermate;
break;
case POWERMATE_PRODUCT_OLD:
input_dev->name = pm_name_soundknob;
break;
default:
input_dev->name = pm_name_soundknob;
printk(KERN_WARNING "powermate: unknown product id %04x\n",
le16_to_cpu(udev->descriptor.idProduct));
}
input_dev->phys = pm->phys;
usb_to_input_id(udev, &input_dev->id);
input_dev->dev.parent = &intf->dev;
input_set_drvdata(input_dev, pm);
input_dev->event = powermate_input_event;
input_dev->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_REL) |
BIT_MASK(EV_MSC);
input_dev->keybit[BIT_WORD(BTN_0)] = BIT_MASK(BTN_0);
input_dev->relbit[BIT_WORD(REL_DIAL)] = BIT_MASK(REL_DIAL);
input_dev->mscbit[BIT_WORD(MSC_PULSELED)] = BIT_MASK(MSC_PULSELED);
/* get a handle to the interrupt data pipe */
pipe = usb_rcvintpipe(udev, endpoint->bEndpointAddress);
maxp = usb_maxpacket(udev, pipe, usb_pipeout(pipe));
if (maxp < POWERMATE_PAYLOAD_SIZE_MIN || maxp > POWERMATE_PAYLOAD_SIZE_MAX) {
printk(KERN_WARNING "powermate: Expected payload of %d--%d bytes, found %d bytes!\n",
POWERMATE_PAYLOAD_SIZE_MIN, POWERMATE_PAYLOAD_SIZE_MAX, maxp);
maxp = POWERMATE_PAYLOAD_SIZE_MAX;
}
usb_fill_int_urb(pm->irq, udev, pipe, pm->data,
maxp, powermate_irq,
pm, endpoint->bInterval);
pm->irq->transfer_dma = pm->data_dma;
pm->irq->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;
/* register our interrupt URB with the USB system */
if (usb_submit_urb(pm->irq, GFP_KERNEL)) {
error = -EIO;
goto fail4;
}
error = input_register_device(pm->input);
if (error)
goto fail5;
/* force an update of everything */
pm->requires_update = UPDATE_PULSE_ASLEEP | UPDATE_PULSE_AWAKE | UPDATE_PULSE_MODE | UPDATE_STATIC_BRIGHTNESS;
powermate_pulse_led(pm, 0x80, 255, 0, 1, 0); // set default pulse parameters
usb_set_intfdata(intf, pm);
return 0;
fail5: usb_kill_urb(pm->irq);
fail4: usb_free_urb(pm->config);
fail3: usb_free_urb(pm->irq);
fail2: powermate_free_buffers(udev, pm);
fail1: input_free_device(input_dev);
kfree(pm);
return error;
}
/* Called when a USB device we've accepted ownership of is removed */
static void powermate_disconnect(struct usb_interface *intf)
{
struct powermate_device *pm = usb_get_intfdata (intf);
usb_set_intfdata(intf, NULL);
if (pm) {
pm->requires_update = 0;
usb_kill_urb(pm->irq);
input_unregister_device(pm->input);
usb_free_urb(pm->irq);
usb_free_urb(pm->config);
powermate_free_buffers(interface_to_usbdev(intf), pm);
kfree(pm);
}
}
static struct usb_device_id powermate_devices [] = {
{ USB_DEVICE(POWERMATE_VENDOR, POWERMATE_PRODUCT_NEW) },
{ USB_DEVICE(POWERMATE_VENDOR, POWERMATE_PRODUCT_OLD) },
{ USB_DEVICE(CONTOUR_VENDOR, CONTOUR_JOG) },
{ } /* Terminating entry */
};
MODULE_DEVICE_TABLE (usb, powermate_devices);
static struct usb_driver powermate_driver = {
.name = "powermate",
.probe = powermate_probe,
.disconnect = powermate_disconnect,
.id_table = powermate_devices,
};
static int __init powermate_init(void)
{
return usb_register(&powermate_driver);
}
static void __exit powermate_cleanup(void)
{
usb_deregister(&powermate_driver);
}
module_init(powermate_init);
module_exit(powermate_cleanup);
MODULE_AUTHOR( "William R Sowerbutts" );
MODULE_DESCRIPTION( "Griffin Technology, Inc PowerMate driver" );
MODULE_LICENSE("GPL");

View File

@@ -0,0 +1,120 @@
/*
* Support for the S1 button on Routerboard 532
*
* Copyright (C) 2009 Phil Sutter <n0-1@freewrt.org>
*/
#include <linux/input-polldev.h>
#include <linux/module.h>
#include <linux/platform_device.h>
#include <asm/mach-rc32434/gpio.h>
#include <asm/mach-rc32434/rb.h>
#define DRV_NAME "rb532-button"
#define RB532_BTN_RATE 100 /* msec */
#define RB532_BTN_KSYM BTN_0
/* The S1 button state is provided by GPIO pin 1. But as this
* pin is also used for uart input as alternate function, the
* operational modes must be switched first:
* 1) disable uart using set_latch_u5()
* 2) turn off alternate function implicitly through
* gpio_direction_input()
* 3) read the GPIO's current value
* 4) undo step 2 by enabling alternate function (in this
* mode the GPIO direction is fixed, so no change needed)
* 5) turn on uart again
* The GPIO value occurs to be inverted, so pin high means
* button is not pressed.
*/
static bool rb532_button_pressed(void)
{
int val;
set_latch_u5(0, LO_FOFF);
gpio_direction_input(GPIO_BTN_S1);
val = gpio_get_value(GPIO_BTN_S1);
rb532_gpio_set_func(GPIO_BTN_S1);
set_latch_u5(LO_FOFF, 0);
return !val;
}
static void rb532_button_poll(struct input_polled_dev *poll_dev)
{
input_report_key(poll_dev->input, RB532_BTN_KSYM,
rb532_button_pressed());
input_sync(poll_dev->input);
}
static int __devinit rb532_button_probe(struct platform_device *pdev)
{
struct input_polled_dev *poll_dev;
int error;
poll_dev = input_allocate_polled_device();
if (!poll_dev)
return -ENOMEM;
poll_dev->poll = rb532_button_poll;
poll_dev->poll_interval = RB532_BTN_RATE;
poll_dev->input->name = "rb532 button";
poll_dev->input->phys = "rb532/button0";
poll_dev->input->id.bustype = BUS_HOST;
poll_dev->input->dev.parent = &pdev->dev;
dev_set_drvdata(&pdev->dev, poll_dev);
input_set_capability(poll_dev->input, EV_KEY, RB532_BTN_KSYM);
error = input_register_polled_device(poll_dev);
if (error) {
input_free_polled_device(poll_dev);
return error;
}
return 0;
}
static int __devexit rb532_button_remove(struct platform_device *pdev)
{
struct input_polled_dev *poll_dev = dev_get_drvdata(&pdev->dev);
input_unregister_polled_device(poll_dev);
input_free_polled_device(poll_dev);
dev_set_drvdata(&pdev->dev, NULL);
return 0;
}
static struct platform_driver rb532_button_driver = {
.probe = rb532_button_probe,
.remove = __devexit_p(rb532_button_remove),
.driver = {
.name = DRV_NAME,
.owner = THIS_MODULE,
},
};
static int __init rb532_button_init(void)
{
return platform_driver_register(&rb532_button_driver);
}
static void __exit rb532_button_exit(void)
{
platform_driver_unregister(&rb532_button_driver);
}
module_init(rb532_button_init);
module_exit(rb532_button_exit);
MODULE_AUTHOR("Phil Sutter <n0-1@freewrt.org>");
MODULE_LICENSE("GPL");
MODULE_DESCRIPTION("Support for S1 button on Routerboard 532");
MODULE_ALIAS("platform:" DRV_NAME);

View File

@@ -0,0 +1,242 @@
/*
* rotary_encoder.c
*
* (c) 2009 Daniel Mack <daniel@caiaq.de>
*
* state machine code inspired by code from Tim Ruetz
*
* A generic driver for rotary encoders connected to GPIO lines.
* See file:Documentation/input/rotary_encoder.txt for more information
*
* 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/module.h>
#include <linux/init.h>
#include <linux/interrupt.h>
#include <linux/input.h>
#include <linux/device.h>
#include <linux/platform_device.h>
#include <linux/gpio.h>
#include <linux/rotary_encoder.h>
#define DRV_NAME "rotary-encoder"
struct rotary_encoder {
struct input_dev *input;
struct rotary_encoder_platform_data *pdata;
unsigned int axis;
unsigned int pos;
unsigned int irq_a;
unsigned int irq_b;
bool armed;
unsigned char dir; /* 0 - clockwise, 1 - CCW */
};
static irqreturn_t rotary_encoder_irq(int irq, void *dev_id)
{
struct rotary_encoder *encoder = dev_id;
struct rotary_encoder_platform_data *pdata = encoder->pdata;
int a = !!gpio_get_value(pdata->gpio_a);
int b = !!gpio_get_value(pdata->gpio_b);
int state;
a ^= pdata->inverted_a;
b ^= pdata->inverted_b;
state = (a << 1) | b;
switch (state) {
case 0x0:
if (!encoder->armed)
break;
if (pdata->relative_axis) {
input_report_rel(encoder->input, pdata->axis,
encoder->dir ? -1 : 1);
} else {
unsigned int pos = encoder->pos;
if (encoder->dir) {
/* turning counter-clockwise */
if (pdata->rollover)
pos += pdata->steps;
if (pos)
pos--;
} else {
/* turning clockwise */
if (pdata->rollover || pos < pdata->steps)
pos++;
}
if (pdata->rollover)
pos %= pdata->steps;
encoder->pos = pos;
input_report_abs(encoder->input, pdata->axis,
encoder->pos);
}
input_sync(encoder->input);
encoder->armed = false;
break;
case 0x1:
case 0x2:
if (encoder->armed)
encoder->dir = state - 1;
break;
case 0x3:
encoder->armed = true;
break;
}
return IRQ_HANDLED;
}
static int __devinit rotary_encoder_probe(struct platform_device *pdev)
{
struct rotary_encoder_platform_data *pdata = pdev->dev.platform_data;
struct rotary_encoder *encoder;
struct input_dev *input;
int err;
if (!pdata) {
dev_err(&pdev->dev, "missing platform data\n");
return -ENOENT;
}
encoder = kzalloc(sizeof(struct rotary_encoder), GFP_KERNEL);
input = input_allocate_device();
if (!encoder || !input) {
dev_err(&pdev->dev, "failed to allocate memory for device\n");
err = -ENOMEM;
goto exit_free_mem;
}
encoder->input = input;
encoder->pdata = pdata;
encoder->irq_a = gpio_to_irq(pdata->gpio_a);
encoder->irq_b = gpio_to_irq(pdata->gpio_b);
/* create and register the input driver */
input->name = pdev->name;
input->id.bustype = BUS_HOST;
input->dev.parent = &pdev->dev;
if (pdata->relative_axis) {
input->evbit[0] = BIT_MASK(EV_REL);
input->relbit[0] = BIT_MASK(pdata->axis);
} else {
input->evbit[0] = BIT_MASK(EV_ABS);
input_set_abs_params(encoder->input,
pdata->axis, 0, pdata->steps, 0, 1);
}
err = input_register_device(input);
if (err) {
dev_err(&pdev->dev, "failed to register input device\n");
goto exit_free_mem;
}
/* request the GPIOs */
err = gpio_request(pdata->gpio_a, DRV_NAME);
if (err) {
dev_err(&pdev->dev, "unable to request GPIO %d\n",
pdata->gpio_a);
goto exit_unregister_input;
}
err = gpio_request(pdata->gpio_b, DRV_NAME);
if (err) {
dev_err(&pdev->dev, "unable to request GPIO %d\n",
pdata->gpio_b);
goto exit_free_gpio_a;
}
/* request the IRQs */
err = request_irq(encoder->irq_a, &rotary_encoder_irq,
IORESOURCE_IRQ_HIGHEDGE | IORESOURCE_IRQ_LOWEDGE,
DRV_NAME, encoder);
if (err) {
dev_err(&pdev->dev, "unable to request IRQ %d\n",
encoder->irq_a);
goto exit_free_gpio_b;
}
err = request_irq(encoder->irq_b, &rotary_encoder_irq,
IORESOURCE_IRQ_HIGHEDGE | IORESOURCE_IRQ_LOWEDGE,
DRV_NAME, encoder);
if (err) {
dev_err(&pdev->dev, "unable to request IRQ %d\n",
encoder->irq_b);
goto exit_free_irq_a;
}
platform_set_drvdata(pdev, encoder);
return 0;
exit_free_irq_a:
free_irq(encoder->irq_a, encoder);
exit_free_gpio_b:
gpio_free(pdata->gpio_b);
exit_free_gpio_a:
gpio_free(pdata->gpio_a);
exit_unregister_input:
input_unregister_device(input);
input = NULL; /* so we don't try to free it */
exit_free_mem:
input_free_device(input);
kfree(encoder);
return err;
}
static int __devexit rotary_encoder_remove(struct platform_device *pdev)
{
struct rotary_encoder *encoder = platform_get_drvdata(pdev);
struct rotary_encoder_platform_data *pdata = pdev->dev.platform_data;
free_irq(encoder->irq_a, encoder);
free_irq(encoder->irq_b, encoder);
gpio_free(pdata->gpio_a);
gpio_free(pdata->gpio_b);
input_unregister_device(encoder->input);
platform_set_drvdata(pdev, NULL);
kfree(encoder);
return 0;
}
static struct platform_driver rotary_encoder_driver = {
.probe = rotary_encoder_probe,
.remove = __devexit_p(rotary_encoder_remove),
.driver = {
.name = DRV_NAME,
.owner = THIS_MODULE,
}
};
static int __init rotary_encoder_init(void)
{
return platform_driver_register(&rotary_encoder_driver);
}
static void __exit rotary_encoder_exit(void)
{
platform_driver_unregister(&rotary_encoder_driver);
}
module_init(rotary_encoder_init);
module_exit(rotary_encoder_exit);
MODULE_ALIAS("platform:" DRV_NAME);
MODULE_DESCRIPTION("GPIO rotary encoder driver");
MODULE_AUTHOR("Daniel Mack <daniel@caiaq.de>");
MODULE_LICENSE("GPL v2");

View File

@@ -0,0 +1,179 @@
/*
* SGI Volume Button interface driver
*
* Copyright (C) 2008 Thomas Bogendoerfer <tsbogend@alpha.franken.de>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include <linux/init.h>
#include <linux/input-polldev.h>
#include <linux/ioport.h>
#include <linux/module.h>
#include <linux/platform_device.h>
#ifdef CONFIG_SGI_IP22
#include <asm/sgi/ioc.h>
static inline u8 button_status(void)
{
u8 status;
status = readb(&sgioc->panel) ^ 0xa0;
return ((status & 0x80) >> 6) | ((status & 0x20) >> 5);
}
#endif
#ifdef CONFIG_SGI_IP32
#include <asm/ip32/mace.h>
static inline u8 button_status(void)
{
u64 status;
status = readq(&mace->perif.audio.control);
writeq(status & ~(3U << 23), &mace->perif.audio.control);
return (status >> 23) & 3;
}
#endif
#define BUTTONS_POLL_INTERVAL 30 /* msec */
#define BUTTONS_COUNT_THRESHOLD 3
static const unsigned short sgi_map[] = {
KEY_VOLUMEDOWN,
KEY_VOLUMEUP
};
struct buttons_dev {
struct input_polled_dev *poll_dev;
unsigned short keymap[ARRAY_SIZE(sgi_map)];
int count[ARRAY_SIZE(sgi_map)];
};
static void handle_buttons(struct input_polled_dev *dev)
{
struct buttons_dev *bdev = dev->private;
struct input_dev *input = dev->input;
u8 status;
int i;
status = button_status();
for (i = 0; i < ARRAY_SIZE(bdev->keymap); i++) {
if (status & (1U << i)) {
if (++bdev->count[i] == BUTTONS_COUNT_THRESHOLD) {
input_event(input, EV_MSC, MSC_SCAN, i);
input_report_key(input, bdev->keymap[i], 1);
input_sync(input);
}
} else {
if (bdev->count[i] >= BUTTONS_COUNT_THRESHOLD) {
input_event(input, EV_MSC, MSC_SCAN, i);
input_report_key(input, bdev->keymap[i], 0);
input_sync(input);
}
bdev->count[i] = 0;
}
}
}
static int __devinit sgi_buttons_probe(struct platform_device *pdev)
{
struct buttons_dev *bdev;
struct input_polled_dev *poll_dev;
struct input_dev *input;
int error, i;
bdev = kzalloc(sizeof(struct buttons_dev), GFP_KERNEL);
poll_dev = input_allocate_polled_device();
if (!bdev || !poll_dev) {
error = -ENOMEM;
goto err_free_mem;
}
memcpy(bdev->keymap, sgi_map, sizeof(bdev->keymap));
poll_dev->private = bdev;
poll_dev->poll = handle_buttons;
poll_dev->poll_interval = BUTTONS_POLL_INTERVAL;
input = poll_dev->input;
input->name = "SGI buttons";
input->phys = "sgi/input0";
input->id.bustype = BUS_HOST;
input->dev.parent = &pdev->dev;
input->keycode = bdev->keymap;
input->keycodemax = ARRAY_SIZE(bdev->keymap);
input->keycodesize = sizeof(unsigned short);
input_set_capability(input, EV_MSC, MSC_SCAN);
__set_bit(EV_KEY, input->evbit);
for (i = 0; i < ARRAY_SIZE(sgi_map); i++)
__set_bit(bdev->keymap[i], input->keybit);
__clear_bit(KEY_RESERVED, input->keybit);
bdev->poll_dev = poll_dev;
dev_set_drvdata(&pdev->dev, bdev);
error = input_register_polled_device(poll_dev);
if (error)
goto err_free_mem;
return 0;
err_free_mem:
input_free_polled_device(poll_dev);
kfree(bdev);
dev_set_drvdata(&pdev->dev, NULL);
return error;
}
static int __devexit sgi_buttons_remove(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
struct buttons_dev *bdev = dev_get_drvdata(dev);
input_unregister_polled_device(bdev->poll_dev);
input_free_polled_device(bdev->poll_dev);
kfree(bdev);
dev_set_drvdata(dev, NULL);
return 0;
}
static struct platform_driver sgi_buttons_driver = {
.probe = sgi_buttons_probe,
.remove = __devexit_p(sgi_buttons_remove),
.driver = {
.name = "sgibtns",
.owner = THIS_MODULE,
},
};
static int __init sgi_buttons_init(void)
{
return platform_driver_register(&sgi_buttons_driver);
}
static void __exit sgi_buttons_exit(void)
{
platform_driver_unregister(&sgi_buttons_driver);
}
MODULE_LICENSE("GPL");
module_init(sgi_buttons_init);
module_exit(sgi_buttons_exit);

View File

@@ -0,0 +1,369 @@
/*
* Driver for PC-speaker like devices found on various Sparc systems.
*
* Copyright (c) 2002 Vojtech Pavlik
* Copyright (c) 2002, 2006, 2008 David S. Miller (davem@davemloft.net)
*/
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/init.h>
#include <linux/input.h>
#include <linux/of_device.h>
#include <asm/io.h>
MODULE_AUTHOR("David S. Miller <davem@davemloft.net>");
MODULE_DESCRIPTION("Sparc Speaker beeper driver");
MODULE_LICENSE("GPL");
struct grover_beep_info {
void __iomem *freq_regs;
void __iomem *enable_reg;
};
struct bbc_beep_info {
u32 clock_freq;
void __iomem *regs;
};
struct sparcspkr_state {
const char *name;
int (*event)(struct input_dev *dev, unsigned int type, unsigned int code, int value);
spinlock_t lock;
struct input_dev *input_dev;
union {
struct grover_beep_info grover;
struct bbc_beep_info bbc;
} u;
};
static u32 bbc_count_to_reg(struct bbc_beep_info *info, unsigned int count)
{
u32 val, clock_freq = info->clock_freq;
int i;
if (!count)
return 0;
if (count <= clock_freq >> 20)
return 1 << 18;
if (count >= clock_freq >> 12)
return 1 << 10;
val = 1 << 18;
for (i = 19; i >= 11; i--) {
val >>= 1;
if (count <= clock_freq >> i)
break;
}
return val;
}
static int bbc_spkr_event(struct input_dev *dev, unsigned int type, unsigned int code, int value)
{
struct sparcspkr_state *state = dev_get_drvdata(dev->dev.parent);
struct bbc_beep_info *info = &state->u.bbc;
unsigned int count = 0;
unsigned long flags;
if (type != EV_SND)
return -1;
switch (code) {
case SND_BELL: if (value) value = 1000;
case SND_TONE: break;
default: return -1;
}
if (value > 20 && value < 32767)
count = 1193182 / value;
count = bbc_count_to_reg(info, count);
spin_lock_irqsave(&state->lock, flags);
if (count) {
outb(0x01, info->regs + 0);
outb(0x00, info->regs + 2);
outb((count >> 16) & 0xff, info->regs + 3);
outb((count >> 8) & 0xff, info->regs + 4);
outb(0x00, info->regs + 5);
} else {
outb(0x00, info->regs + 0);
}
spin_unlock_irqrestore(&state->lock, flags);
return 0;
}
static int grover_spkr_event(struct input_dev *dev, unsigned int type, unsigned int code, int value)
{
struct sparcspkr_state *state = dev_get_drvdata(dev->dev.parent);
struct grover_beep_info *info = &state->u.grover;
unsigned int count = 0;
unsigned long flags;
if (type != EV_SND)
return -1;
switch (code) {
case SND_BELL: if (value) value = 1000;
case SND_TONE: break;
default: return -1;
}
if (value > 20 && value < 32767)
count = 1193182 / value;
spin_lock_irqsave(&state->lock, flags);
if (count) {
/* enable counter 2 */
outb(inb(info->enable_reg) | 3, info->enable_reg);
/* set command for counter 2, 2 byte write */
outb(0xB6, info->freq_regs + 1);
/* select desired HZ */
outb(count & 0xff, info->freq_regs + 0);
outb((count >> 8) & 0xff, info->freq_regs + 0);
} else {
/* disable counter 2 */
outb(inb_p(info->enable_reg) & 0xFC, info->enable_reg);
}
spin_unlock_irqrestore(&state->lock, flags);
return 0;
}
static int __devinit sparcspkr_probe(struct device *dev)
{
struct sparcspkr_state *state = dev_get_drvdata(dev);
struct input_dev *input_dev;
int error;
input_dev = input_allocate_device();
if (!input_dev)
return -ENOMEM;
input_dev->name = state->name;
input_dev->phys = "sparc/input0";
input_dev->id.bustype = BUS_ISA;
input_dev->id.vendor = 0x001f;
input_dev->id.product = 0x0001;
input_dev->id.version = 0x0100;
input_dev->dev.parent = dev;
input_dev->evbit[0] = BIT_MASK(EV_SND);
input_dev->sndbit[0] = BIT_MASK(SND_BELL) | BIT_MASK(SND_TONE);
input_dev->event = state->event;
error = input_register_device(input_dev);
if (error) {
input_free_device(input_dev);
return error;
}
state->input_dev = input_dev;
return 0;
}
static int sparcspkr_shutdown(struct of_device *dev)
{
struct sparcspkr_state *state = dev_get_drvdata(&dev->dev);
struct input_dev *input_dev = state->input_dev;
/* turn off the speaker */
state->event(input_dev, EV_SND, SND_BELL, 0);
return 0;
}
static int __devinit bbc_beep_probe(struct of_device *op, const struct of_device_id *match)
{
struct sparcspkr_state *state;
struct bbc_beep_info *info;
struct device_node *dp;
int err = -ENOMEM;
state = kzalloc(sizeof(*state), GFP_KERNEL);
if (!state)
goto out_err;
state->name = "Sparc BBC Speaker";
state->event = bbc_spkr_event;
spin_lock_init(&state->lock);
dp = of_find_node_by_path("/");
err = -ENODEV;
if (!dp)
goto out_free;
info = &state->u.bbc;
info->clock_freq = of_getintprop_default(dp, "clock-frequency", 0);
if (!info->clock_freq)
goto out_free;
info->regs = of_ioremap(&op->resource[0], 0, 6, "bbc beep");
if (!info->regs)
goto out_free;
dev_set_drvdata(&op->dev, state);
err = sparcspkr_probe(&op->dev);
if (err)
goto out_clear_drvdata;
return 0;
out_clear_drvdata:
dev_set_drvdata(&op->dev, NULL);
of_iounmap(&op->resource[0], info->regs, 6);
out_free:
kfree(state);
out_err:
return err;
}
static int __devexit bbc_remove(struct of_device *op)
{
struct sparcspkr_state *state = dev_get_drvdata(&op->dev);
struct input_dev *input_dev = state->input_dev;
struct bbc_beep_info *info = &state->u.bbc;
/* turn off the speaker */
state->event(input_dev, EV_SND, SND_BELL, 0);
input_unregister_device(input_dev);
of_iounmap(&op->resource[0], info->regs, 6);
dev_set_drvdata(&op->dev, NULL);
kfree(state);
return 0;
}
static const struct of_device_id bbc_beep_match[] = {
{
.name = "beep",
.compatible = "SUNW,bbc-beep",
},
{},
};
static struct of_platform_driver bbc_beep_driver = {
.name = "bbcbeep",
.match_table = bbc_beep_match,
.probe = bbc_beep_probe,
.remove = __devexit_p(bbc_remove),
.shutdown = sparcspkr_shutdown,
};
static int __devinit grover_beep_probe(struct of_device *op, const struct of_device_id *match)
{
struct sparcspkr_state *state;
struct grover_beep_info *info;
int err = -ENOMEM;
state = kzalloc(sizeof(*state), GFP_KERNEL);
if (!state)
goto out_err;
state->name = "Sparc Grover Speaker";
state->event = grover_spkr_event;
spin_lock_init(&state->lock);
info = &state->u.grover;
info->freq_regs = of_ioremap(&op->resource[2], 0, 2, "grover beep freq");
if (!info->freq_regs)
goto out_free;
info->enable_reg = of_ioremap(&op->resource[3], 0, 1, "grover beep enable");
if (!info->enable_reg)
goto out_unmap_freq_regs;
dev_set_drvdata(&op->dev, state);
err = sparcspkr_probe(&op->dev);
if (err)
goto out_clear_drvdata;
return 0;
out_clear_drvdata:
dev_set_drvdata(&op->dev, NULL);
of_iounmap(&op->resource[3], info->enable_reg, 1);
out_unmap_freq_regs:
of_iounmap(&op->resource[2], info->freq_regs, 2);
out_free:
kfree(state);
out_err:
return err;
}
static int __devexit grover_remove(struct of_device *op)
{
struct sparcspkr_state *state = dev_get_drvdata(&op->dev);
struct grover_beep_info *info = &state->u.grover;
struct input_dev *input_dev = state->input_dev;
/* turn off the speaker */
state->event(input_dev, EV_SND, SND_BELL, 0);
input_unregister_device(input_dev);
of_iounmap(&op->resource[3], info->enable_reg, 1);
of_iounmap(&op->resource[2], info->freq_regs, 2);
dev_set_drvdata(&op->dev, NULL);
kfree(state);
return 0;
}
static const struct of_device_id grover_beep_match[] = {
{
.name = "beep",
.compatible = "SUNW,smbus-beep",
},
{},
};
static struct of_platform_driver grover_beep_driver = {
.name = "groverbeep",
.match_table = grover_beep_match,
.probe = grover_beep_probe,
.remove = __devexit_p(grover_remove),
.shutdown = sparcspkr_shutdown,
};
static int __init sparcspkr_init(void)
{
int err = of_register_driver(&bbc_beep_driver,
&of_platform_bus_type);
if (!err) {
err = of_register_driver(&grover_beep_driver,
&of_platform_bus_type);
if (err)
of_unregister_driver(&bbc_beep_driver);
}
return err;
}
static void __exit sparcspkr_exit(void)
{
of_unregister_driver(&bbc_beep_driver);
of_unregister_driver(&grover_beep_driver);
}
module_init(sparcspkr_init);
module_exit(sparcspkr_exit);

View File

@@ -0,0 +1,520 @@
/*
* Copyright (C) 2009 STMicroelectronics Limited
* Author: Pawel Moll <pawel.moll@st.com>
*
* May be copied or modified under the terms of the GNU General Public
* License. See linux/COPYING for more information.
*
* Driver for Shenzen Titan Micro Electronics TM1668 LED/keyboard driver.
*
* See <include/linux/tm1668.h> file for platform data definitions.
*
* /sys/.../tm1668/ files:
* /text - displays written string (via platform characters table)
* /keys - returns keyboard state (to be used as mask in key definition)
* /raw - raw keyboard (read) and display (write) buffers access
*/
#include <linux/init.h>
#include <linux/module.h>
#include <linux/platform_device.h>
#include <linux/workqueue.h>
#include <linux/ctype.h>
#include <linux/delay.h>
#include <linux/input.h>
#include <linux/gpio.h>
#include <linux/tm1668.h>
#define DRIVER_NAME "tm1668"
#define TM1668_DISPLAY_BUFFER_SIZE 14
#define TM1668_DISPLAY_MAX_DIGITS 7
#define TM1668_KEYBOARD_BUFFER_SIZE 4
#define TM1668_CMD_DISPLAY_MODE(mode_6_12_not_7_11) \
(0x00 | \
(mode_6_12_not_7_11 ? 0x2 : 0x3))
#define TM1668_CMD_DATA(test_not_normal_mode, display_address_increment, \
read_not_write) \
(0x40 | \
(test_not_normal_mode ? 0x4 : 0x0) | \
(display_address_increment ? 0x0 : 0x1) | \
(read_not_write ? 0x2 : 0x0))
#define TM1668_CMD_ADDRESS(address) \
(0xc0 | \
(address & 0xf))
#define TM1668_CMD_DISPLAY_CONTROL(on, pulse_width) \
(0x80 | \
(on ? 0x8 : 0x0) | \
(pulse_width & 0x7))
struct tm1668_chip {
spinlock_t lock; /* access lock */
unsigned gpio_dio, gpio_sclk, gpio_stb;
int brightness;
int characters_num;
struct tm1668_character *characters;
struct input_dev *input;
u32 keys_prev;
int keys_num;
struct tm1668_key *keys;
struct delayed_work keys_work;
unsigned long keys_poll_period;
};
/* Serial interface */
static void tm1668_writeb(struct tm1668_chip *chip, u8 byte)
{
int i;
for (i = 0; i < 8; i++) {
gpio_set_value(chip->gpio_dio, byte & 0x1);
gpio_set_value(chip->gpio_sclk, 0);
udelay(1);
gpio_set_value(chip->gpio_sclk, 1);
udelay(1);
byte >>= 1;
}
}
static void tm1668_readb(struct tm1668_chip *chip, u8 *byte)
{
int i;
*byte = 0;
for (i = 0; i < 8; i++) {
gpio_set_value(chip->gpio_sclk, 0);
udelay(1);
*byte |= gpio_get_value(chip->gpio_dio);
gpio_set_value(chip->gpio_sclk, 1);
udelay(1);
*byte <<= 1;
}
}
static void tm1668_send(struct tm1668_chip *chip, u8 command,
const void *buf, int len)
{
const u8 *data = buf;
int i;
BUG_ON(len > TM1668_DISPLAY_BUFFER_SIZE);
gpio_set_value(chip->gpio_stb, 0);
udelay(1);
tm1668_writeb(chip, command);
for (i = 0; i < len; i++)
tm1668_writeb(chip, *data++);
udelay(1);
gpio_set_value(chip->gpio_stb, 1);
udelay(2);
}
static void tm1668_recv(struct tm1668_chip *chip, u8 command,
void *buf, int len)
{
u8 *data = buf;
int i;
gpio_set_value(chip->gpio_stb, 0);
udelay(1);
tm1668_writeb(chip, command);
udelay(1);
gpio_direction_input(chip->gpio_dio);
for (i = 0; i < len; i++)
tm1668_readb(chip, data++);
gpio_direction_output(chip->gpio_dio, 1);
udelay(1);
gpio_set_value(chip->gpio_stb, 1);
udelay(2);
}
/* Keyboard input */
static void tm1668_keys_poll(struct work_struct *work)
{
struct tm1668_chip *chip = container_of(work, struct tm1668_chip,
keys_work.work);
u32 keys, diff;
int i;
spin_lock(&chip->lock);
tm1668_recv(chip, TM1668_CMD_DATA(0, 1, 1), &keys, sizeof(keys));
spin_unlock(&chip->lock);
diff = keys ^ chip->keys_prev;
for (i = 0; i < chip->keys_num; i++) {
struct tm1668_key *key = &chip->keys[i];
if (diff & key->mask) {
input_event(chip->input, EV_KEY, key->code,
!!(keys & key->mask));
input_sync(chip->input);
}
}
chip->keys_prev = keys;
schedule_delayed_work(&chip->keys_work, chip->keys_poll_period);
}
static ssize_t tm1668_keys_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
struct tm1668_chip *chip = dev_get_drvdata(dev);
u32 keys;
spin_lock(&chip->lock);
tm1668_recv(chip, TM1668_CMD_DATA(0, 1, 1), &keys, sizeof(keys));
spin_unlock(&chip->lock);
return sprintf(buf, "0x%08x\n", keys);
}
static DEVICE_ATTR(keys, S_IRUSR, tm1668_keys_show, NULL);
/* Display control & sysfs interface */
static void tm1668_clear(struct tm1668_chip *chip)
{
u8 data[TM1668_DISPLAY_BUFFER_SIZE] = { 0, };
tm1668_send(chip, TM1668_CMD_DATA(0, 1, 0), NULL, 0);
tm1668_send(chip, TM1668_CMD_ADDRESS(0), data, sizeof(data));
}
static void tm1668_set_brightness(struct tm1668_chip *chip, int brightness)
{
int enabled = brightness > 0;
if (brightness < 0)
brightness = 0;
else if (brightness > 8)
brightness = 8;
chip->brightness = brightness;
if (enabled)
brightness--;
tm1668_send(chip, TM1668_CMD_DISPLAY_CONTROL(enabled, brightness),
NULL, 0);
}
static ssize_t tm1668_brightness_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
int result;
struct tm1668_chip *chip = dev_get_drvdata(dev);
spin_lock(&chip->lock);
result = sprintf(buf, "%d\n", chip->brightness);
spin_unlock(&chip->lock);
return result;
}
static ssize_t tm1668_brightness_store(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t size)
{
ssize_t result;
struct tm1668_chip *chip = dev_get_drvdata(dev);
unsigned long value;
result = strict_strtoul(buf, 10, &value);
if (result == 0) {
spin_lock(&chip->lock);
tm1668_set_brightness(chip, value);
spin_unlock(&chip->lock);
result = size;
}
return result;
}
static DEVICE_ATTR(brightness, S_IRUSR | S_IWUSR, tm1668_brightness_show,
tm1668_brightness_store);
static void tm1668_print(struct tm1668_chip *chip, const char *text)
{
u16 data[TM1668_DISPLAY_MAX_DIGITS] = { 0, };
int i, j;
BUG_ON(chip->characters_num <= 0 || !chip->characters);
BUG_ON(!text);
for (i = 0; i < TM1668_DISPLAY_MAX_DIGITS && *text; i++, text++)
for (j = 0; j < chip->characters_num; j++)
if (chip->characters[j].character == *text) {
data[i] = chip->characters[j].value;
break;
}
tm1668_send(chip, TM1668_CMD_DATA(0, 1, 0), NULL, 0);
tm1668_send(chip, TM1668_CMD_ADDRESS(0), data, sizeof(data));
}
static ssize_t tm1668_text_store(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t size)
{
struct tm1668_chip *chip = dev_get_drvdata(dev);
spin_lock(&chip->lock);
tm1668_print(chip, buf);
spin_unlock(&chip->lock);
return size;
}
static DEVICE_ATTR(text, S_IWUSR, NULL, tm1668_text_store);
/* raw sysfs interface */
static ssize_t tm1668_raw_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
struct tm1668_chip *chip = dev_get_drvdata(dev);
spin_lock(&chip->lock);
tm1668_recv(chip, TM1668_CMD_DATA(0, 1, 1), buf,
TM1668_KEYBOARD_BUFFER_SIZE);
spin_unlock(&chip->lock);
return TM1668_KEYBOARD_BUFFER_SIZE;
}
static ssize_t tm1668_raw_store(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t size)
{
struct tm1668_chip *chip = dev_get_drvdata(dev);
u8 data[TM1668_DISPLAY_BUFFER_SIZE] = { 0, };
strncpy(data, buf, TM1668_DISPLAY_BUFFER_SIZE);
spin_lock(&chip->lock);
tm1668_send(chip, TM1668_CMD_DATA(0, 1, 0), NULL, 0);
tm1668_send(chip, TM1668_CMD_ADDRESS(0), data, sizeof(data));
spin_unlock(&chip->lock);
return size;
}
static DEVICE_ATTR(raw, S_IRUSR | S_IWUSR, tm1668_raw_show, tm1668_raw_store);
/* Driver routines */
static int __init tm1668_probe(struct platform_device *pdev)
{
int err;
struct tm1668_chip *chip;
struct tm1668_platform_data *plat_data = pdev->dev.platform_data;
int i;
BUG_ON(!plat_data);
chip = kzalloc(sizeof(*chip), GFP_KERNEL);
if (!chip) {
err = -ENOMEM;
goto error_kzalloc;
}
spin_lock_init(&chip->lock);
platform_set_drvdata(pdev, chip);
/* Allocate GPIO lines... */
chip->gpio_dio = plat_data->gpio_dio;
err = gpio_request(chip->gpio_dio, dev_name(&pdev->dev));
if (err != 0)
goto error_request_gpio_dio;
gpio_direction_output(chip->gpio_dio, 1);
chip->gpio_sclk = plat_data->gpio_sclk;
err = gpio_request(chip->gpio_sclk, dev_name(&pdev->dev));
if (err != 0)
goto error_request_gpio_sclk;
gpio_direction_output(chip->gpio_sclk, 1);
chip->gpio_stb = plat_data->gpio_stb;
err = gpio_request(chip->gpio_stb, dev_name(&pdev->dev));
if (err != 0)
goto error_request_gpio_stb;
gpio_direction_output(chip->gpio_stb, 1);
/* Initialize the chip */
tm1668_send(chip, TM1668_CMD_DISPLAY_MODE(plat_data->config ==
tm1668_config_6_digits_12_segments), NULL, 0);
/* Initialize keyboard interface */
chip->input = input_allocate_device();
if (!chip->input) {
err = -ENOMEM;
goto error_input_allocate;
}
chip->input->evbit[0] = BIT(EV_KEY);
chip->input->name = pdev->name;
chip->input->phys = DRIVER_NAME "/input0";
chip->input->dev.parent = &pdev->dev;
chip->input->id.bustype = BUS_HOST;
chip->input->id.vendor = 0x0001;
chip->input->id.product = 0x1668;
chip->input->id.version = 0x0100;
tm1668_recv(chip, TM1668_CMD_DATA(0, 1, 1), &chip->keys_prev,
sizeof(chip->keys_prev));
chip->keys_num = plat_data->keys_num;
chip->keys = plat_data->keys;
BUG_ON(chip->keys_num > 0 && !chip->keys);
for (i = 0; i < chip->keys_num; i++)
input_set_capability(chip->input, EV_KEY, chip->keys[i].code);
err = input_register_device(chip->input);
if (err) {
input_free_device(chip->input);
goto error_input_register;
}
chip->keys_poll_period = plat_data->keys_poll_period;
BUG_ON(chip->keys_poll_period < 1); /* pick your number ;-) */
INIT_DELAYED_WORK(&chip->keys_work, tm1668_keys_poll);
schedule_delayed_work(&chip->keys_work, chip->keys_poll_period);
err = device_create_file(&pdev->dev, &dev_attr_keys);
if (err != 0)
goto error_create_attr_keys;
/* Initialize display interface */
tm1668_clear(chip);
tm1668_set_brightness(chip, plat_data->brightness);
chip->characters_num = plat_data->characters_num;
chip->characters = plat_data->characters;
if (chip->characters_num > 0)
tm1668_print(chip, plat_data->text);
err = device_create_file(&pdev->dev, &dev_attr_brightness);
if (err != 0)
goto error_create_attr_brightness;
if (chip->characters_num > 0) {
err = device_create_file(&pdev->dev, &dev_attr_text);
if (err != 0)
goto error_create_attr_text;
}
/* Initialize raw interface */
err = device_create_file(&pdev->dev, &dev_attr_raw);
if (err != 0)
goto error_create_attr_raw;
return 0;
error_create_attr_raw:
if (chip->characters_num > 0)
device_remove_file(&pdev->dev, &dev_attr_text);
error_create_attr_text:
device_remove_file(&pdev->dev, &dev_attr_brightness);
error_create_attr_brightness:
device_remove_file(&pdev->dev, &dev_attr_keys);
error_create_attr_keys:
cancel_delayed_work_sync(&chip->keys_work);
input_unregister_device(chip->input);
error_input_register:
error_input_allocate:
gpio_free(chip->gpio_stb);
error_request_gpio_stb:
gpio_free(chip->gpio_sclk);
error_request_gpio_sclk:
gpio_free(chip->gpio_dio);
error_request_gpio_dio:
kfree(chip);
error_kzalloc:
return err;
}
static int __exit tm1668_remove(struct platform_device *pdev)
{
struct tm1668_chip *chip = platform_get_drvdata(pdev);
device_remove_file(&pdev->dev, &dev_attr_raw);
if (chip->characters_num > 0)
device_remove_file(&pdev->dev, &dev_attr_text);
device_remove_file(&pdev->dev, &dev_attr_brightness);
tm1668_set_brightness(chip, 0);
device_remove_file(&pdev->dev, &dev_attr_keys);
cancel_delayed_work_sync(&chip->keys_work);
input_unregister_device(chip->input);
kfree(chip);
return 0;
}
static struct platform_driver tm1668_driver = {
.driver = {
.name = DRIVER_NAME,
.owner = THIS_MODULE,
},
.remove = __exit_p(tm1668_remove),
};
static int __init tm1668_init(void)
{
return platform_driver_probe(&tm1668_driver, tm1668_probe);
}
module_init(tm1668_init);
static void __exit tm1668_exit(void)
{
platform_driver_unregister(&tm1668_driver);
}
module_exit(tm1668_exit);
MODULE_AUTHOR("Pawel Moll <pawel.moll@st.com>");
MODULE_DESCRIPTION("TM1668 LED/keyboard chip driver");
MODULE_LICENSE("GPL");

View File

@@ -0,0 +1,145 @@
/**
* twl4030-pwrbutton.c - TWL4030 Power Button Input Driver
*
* Copyright (C) 2008-2009 Nokia Corporation
*
* Written by Peter De Schrijver <peter.de-schrijver@nokia.com>
* Several fixes by Felipe Balbi <felipe.balbi@nokia.com>
*
* 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 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
*/
#include <linux/module.h>
#include <linux/init.h>
#include <linux/kernel.h>
#include <linux/errno.h>
#include <linux/input.h>
#include <linux/interrupt.h>
#include <linux/platform_device.h>
#include <linux/i2c/twl4030.h>
#define PWR_PWRON_IRQ (1 << 0)
#define STS_HW_CONDITIONS 0xf
static irqreturn_t powerbutton_irq(int irq, void *_pwr)
{
struct input_dev *pwr = _pwr;
int err;
u8 value;
#ifdef CONFIG_LOCKDEP
/* WORKAROUND for lockdep forcing IRQF_DISABLED on us, which
* we don't want and can't tolerate since this is a threaded
* IRQ and can sleep due to the i2c reads it has to issue.
* Although it might be friendlier not to borrow this thread
* context...
*/
local_irq_enable();
#endif
err = twl4030_i2c_read_u8(TWL4030_MODULE_PM_MASTER, &value,
STS_HW_CONDITIONS);
if (!err) {
input_report_key(pwr, KEY_POWER, value & PWR_PWRON_IRQ);
input_sync(pwr);
} else {
dev_err(pwr->dev.parent, "twl4030: i2c error %d while reading"
" TWL4030 PM_MASTER STS_HW_CONDITIONS register\n", err);
}
return IRQ_HANDLED;
}
static int __devinit twl4030_pwrbutton_probe(struct platform_device *pdev)
{
struct input_dev *pwr;
int irq = platform_get_irq(pdev, 0);
int err;
pwr = input_allocate_device();
if (!pwr) {
dev_dbg(&pdev->dev, "Can't allocate power button\n");
return -ENOMEM;
}
pwr->evbit[0] = BIT_MASK(EV_KEY);
pwr->keybit[BIT_WORD(KEY_POWER)] = BIT_MASK(KEY_POWER);
pwr->name = "twl4030_pwrbutton";
pwr->phys = "twl4030_pwrbutton/input0";
pwr->dev.parent = &pdev->dev;
err = request_irq(irq, powerbutton_irq,
IRQF_TRIGGER_FALLING | IRQF_TRIGGER_RISING,
"twl4030_pwrbutton", pwr);
if (err < 0) {
dev_dbg(&pdev->dev, "Can't get IRQ for pwrbutton: %d\n", err);
goto free_input_dev;
}
err = input_register_device(pwr);
if (err) {
dev_dbg(&pdev->dev, "Can't register power button: %d\n", err);
goto free_irq;
}
platform_set_drvdata(pdev, pwr);
return 0;
free_irq:
free_irq(irq, NULL);
free_input_dev:
input_free_device(pwr);
return err;
}
static int __devexit twl4030_pwrbutton_remove(struct platform_device *pdev)
{
struct input_dev *pwr = platform_get_drvdata(pdev);
int irq = platform_get_irq(pdev, 0);
free_irq(irq, pwr);
input_unregister_device(pwr);
return 0;
}
struct platform_driver twl4030_pwrbutton_driver = {
.probe = twl4030_pwrbutton_probe,
.remove = __devexit_p(twl4030_pwrbutton_remove),
.driver = {
.name = "twl4030_pwrbutton",
.owner = THIS_MODULE,
},
};
static int __init twl4030_pwrbutton_init(void)
{
return platform_driver_register(&twl4030_pwrbutton_driver);
}
module_init(twl4030_pwrbutton_init);
static void __exit twl4030_pwrbutton_exit(void)
{
platform_driver_unregister(&twl4030_pwrbutton_driver);
}
module_exit(twl4030_pwrbutton_exit);
MODULE_ALIAS("platform:twl4030_pwrbutton");
MODULE_DESCRIPTION("Triton2 Power Button");
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Peter De Schrijver <peter.de-schrijver@nokia.com>");
MODULE_AUTHOR("Felipe Balbi <felipe.balbi@nokia.com>");

View File

@@ -0,0 +1,829 @@
/*
* User level driver support for input subsystem
*
* Heavily based on evdev.c by Vojtech Pavlik
*
* 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
*
* Author: Aristeu Sergio Rozanski Filho <aris@cathedrallabs.org>
*
* Changes/Revisions:
* 0.3 09/04/2006 (Anssi Hannula <anssi.hannula@gmail.com>)
* - updated ff support for the changes in kernel interface
* - added MODULE_VERSION
* 0.2 16/10/2004 (Micah Dowty <micah@navi.cx>)
* - added force feedback support
* - added UI_SET_PHYS
* 0.1 20/06/2002
* - first public version
*/
#include <linux/poll.h>
#include <linux/sched.h>
#include <linux/slab.h>
#include <linux/module.h>
#include <linux/init.h>
#include <linux/smp_lock.h>
#include <linux/fs.h>
#include <linux/miscdevice.h>
#include <linux/uinput.h>
#include "../input-compat.h"
static int uinput_dev_event(struct input_dev *dev, unsigned int type, unsigned int code, int value)
{
struct uinput_device *udev = input_get_drvdata(dev);
udev->buff[udev->head].type = type;
udev->buff[udev->head].code = code;
udev->buff[udev->head].value = value;
do_gettimeofday(&udev->buff[udev->head].time);
udev->head = (udev->head + 1) % UINPUT_BUFFER_SIZE;
wake_up_interruptible(&udev->waitq);
return 0;
}
/* Atomically allocate an ID for the given request. Returns 0 on success. */
static int uinput_request_alloc_id(struct uinput_device *udev, struct uinput_request *request)
{
int id;
int err = -1;
spin_lock(&udev->requests_lock);
for (id = 0; id < UINPUT_NUM_REQUESTS; id++) {
if (!udev->requests[id]) {
request->id = id;
udev->requests[id] = request;
err = 0;
break;
}
}
spin_unlock(&udev->requests_lock);
return err;
}
static struct uinput_request *uinput_request_find(struct uinput_device *udev, int id)
{
/* Find an input request, by ID. Returns NULL if the ID isn't valid. */
if (id >= UINPUT_NUM_REQUESTS || id < 0)
return NULL;
return udev->requests[id];
}
static inline int uinput_request_reserve_slot(struct uinput_device *udev, struct uinput_request *request)
{
/* Allocate slot. If none are available right away, wait. */
return wait_event_interruptible(udev->requests_waitq,
!uinput_request_alloc_id(udev, request));
}
static void uinput_request_done(struct uinput_device *udev, struct uinput_request *request)
{
/* Mark slot as available */
udev->requests[request->id] = NULL;
wake_up(&udev->requests_waitq);
complete(&request->done);
}
static int uinput_request_submit(struct uinput_device *udev, struct uinput_request *request)
{
int retval;
retval = uinput_request_reserve_slot(udev, request);
if (retval)
return retval;
retval = mutex_lock_interruptible(&udev->mutex);
if (retval)
return retval;
if (udev->state != UIST_CREATED) {
retval = -ENODEV;
goto out;
}
/* Tell our userspace app about this new request by queueing an input event */
uinput_dev_event(udev->dev, EV_UINPUT, request->code, request->id);
out:
mutex_unlock(&udev->mutex);
return retval;
}
/*
* Fail all ouitstanding requests so handlers don't wait for the userspace
* to finish processing them.
*/
static void uinput_flush_requests(struct uinput_device *udev)
{
struct uinput_request *request;
int i;
spin_lock(&udev->requests_lock);
for (i = 0; i < UINPUT_NUM_REQUESTS; i++) {
request = udev->requests[i];
if (request) {
request->retval = -ENODEV;
uinput_request_done(udev, request);
}
}
spin_unlock(&udev->requests_lock);
}
static void uinput_dev_set_gain(struct input_dev *dev, u16 gain)
{
uinput_dev_event(dev, EV_FF, FF_GAIN, gain);
}
static void uinput_dev_set_autocenter(struct input_dev *dev, u16 magnitude)
{
uinput_dev_event(dev, EV_FF, FF_AUTOCENTER, magnitude);
}
static int uinput_dev_playback(struct input_dev *dev, int effect_id, int value)
{
return uinput_dev_event(dev, EV_FF, effect_id, value);
}
static int uinput_dev_upload_effect(struct input_dev *dev, struct ff_effect *effect, struct ff_effect *old)
{
struct uinput_device *udev = input_get_drvdata(dev);
struct uinput_request request;
int retval;
/*
* uinput driver does not currently support periodic effects with
* custom waveform since it does not have a way to pass buffer of
* samples (custom_data) to userspace. If ever there is a device
* supporting custom waveforms we would need to define an additional
* ioctl (UI_UPLOAD_SAMPLES) but for now we just bail out.
*/
if (effect->type == FF_PERIODIC &&
effect->u.periodic.waveform == FF_CUSTOM)
return -EINVAL;
request.id = -1;
init_completion(&request.done);
request.code = UI_FF_UPLOAD;
request.u.upload.effect = effect;
request.u.upload.old = old;
retval = uinput_request_submit(udev, &request);
if (!retval) {
wait_for_completion(&request.done);
retval = request.retval;
}
return retval;
}
static int uinput_dev_erase_effect(struct input_dev *dev, int effect_id)
{
struct uinput_device *udev = input_get_drvdata(dev);
struct uinput_request request;
int retval;
if (!test_bit(EV_FF, dev->evbit))
return -ENOSYS;
request.id = -1;
init_completion(&request.done);
request.code = UI_FF_ERASE;
request.u.effect_id = effect_id;
retval = uinput_request_submit(udev, &request);
if (!retval) {
wait_for_completion(&request.done);
retval = request.retval;
}
return retval;
}
static void uinput_destroy_device(struct uinput_device *udev)
{
const char *name, *phys;
struct input_dev *dev = udev->dev;
enum uinput_state old_state = udev->state;
udev->state = UIST_NEW_DEVICE;
if (dev) {
name = dev->name;
phys = dev->phys;
if (old_state == UIST_CREATED) {
uinput_flush_requests(udev);
input_unregister_device(dev);
} else {
input_free_device(dev);
}
kfree(name);
kfree(phys);
udev->dev = NULL;
}
}
static int uinput_create_device(struct uinput_device *udev)
{
struct input_dev *dev = udev->dev;
int error;
if (udev->state != UIST_SETUP_COMPLETE) {
printk(KERN_DEBUG "%s: write device info first\n", UINPUT_NAME);
return -EINVAL;
}
if (udev->ff_effects_max) {
error = input_ff_create(dev, udev->ff_effects_max);
if (error)
goto fail1;
dev->ff->upload = uinput_dev_upload_effect;
dev->ff->erase = uinput_dev_erase_effect;
dev->ff->playback = uinput_dev_playback;
dev->ff->set_gain = uinput_dev_set_gain;
dev->ff->set_autocenter = uinput_dev_set_autocenter;
}
error = input_register_device(udev->dev);
if (error)
goto fail2;
udev->state = UIST_CREATED;
return 0;
fail2: input_ff_destroy(dev);
fail1: uinput_destroy_device(udev);
return error;
}
static int uinput_open(struct inode *inode, struct file *file)
{
struct uinput_device *newdev;
newdev = kzalloc(sizeof(struct uinput_device), GFP_KERNEL);
if (!newdev)
return -ENOMEM;
lock_kernel();
mutex_init(&newdev->mutex);
spin_lock_init(&newdev->requests_lock);
init_waitqueue_head(&newdev->requests_waitq);
init_waitqueue_head(&newdev->waitq);
newdev->state = UIST_NEW_DEVICE;
file->private_data = newdev;
unlock_kernel();
return 0;
}
static int uinput_validate_absbits(struct input_dev *dev)
{
unsigned int cnt;
int retval = 0;
for (cnt = 0; cnt < ABS_MAX + 1; cnt++) {
if (!test_bit(cnt, dev->absbit))
continue;
if ((dev->absmax[cnt] <= dev->absmin[cnt])) {
printk(KERN_DEBUG
"%s: invalid abs[%02x] min:%d max:%d\n",
UINPUT_NAME, cnt,
dev->absmin[cnt], dev->absmax[cnt]);
retval = -EINVAL;
break;
}
if (dev->absflat[cnt] > (dev->absmax[cnt] - dev->absmin[cnt])) {
printk(KERN_DEBUG
"%s: absflat[%02x] out of range: %d "
"(min:%d/max:%d)\n",
UINPUT_NAME, cnt, dev->absflat[cnt],
dev->absmin[cnt], dev->absmax[cnt]);
retval = -EINVAL;
break;
}
}
return retval;
}
static int uinput_allocate_device(struct uinput_device *udev)
{
udev->dev = input_allocate_device();
if (!udev->dev)
return -ENOMEM;
udev->dev->event = uinput_dev_event;
input_set_drvdata(udev->dev, udev);
return 0;
}
static int uinput_setup_device(struct uinput_device *udev, const char __user *buffer, size_t count)
{
struct uinput_user_dev *user_dev;
struct input_dev *dev;
char *name;
int size;
int retval;
if (count != sizeof(struct uinput_user_dev))
return -EINVAL;
if (!udev->dev) {
retval = uinput_allocate_device(udev);
if (retval)
return retval;
}
dev = udev->dev;
user_dev = kmalloc(sizeof(struct uinput_user_dev), GFP_KERNEL);
if (!user_dev)
return -ENOMEM;
if (copy_from_user(user_dev, buffer, sizeof(struct uinput_user_dev))) {
retval = -EFAULT;
goto exit;
}
udev->ff_effects_max = user_dev->ff_effects_max;
size = strnlen(user_dev->name, UINPUT_MAX_NAME_SIZE) + 1;
if (!size) {
retval = -EINVAL;
goto exit;
}
kfree(dev->name);
dev->name = name = kmalloc(size, GFP_KERNEL);
if (!name) {
retval = -ENOMEM;
goto exit;
}
strlcpy(name, user_dev->name, size);
dev->id.bustype = user_dev->id.bustype;
dev->id.vendor = user_dev->id.vendor;
dev->id.product = user_dev->id.product;
dev->id.version = user_dev->id.version;
size = sizeof(int) * (ABS_MAX + 1);
memcpy(dev->absmax, user_dev->absmax, size);
memcpy(dev->absmin, user_dev->absmin, size);
memcpy(dev->absfuzz, user_dev->absfuzz, size);
memcpy(dev->absflat, user_dev->absflat, size);
/* check if absmin/absmax/absfuzz/absflat are filled as
* told in Documentation/input/input-programming.txt */
if (test_bit(EV_ABS, dev->evbit)) {
retval = uinput_validate_absbits(dev);
if (retval < 0)
goto exit;
}
udev->state = UIST_SETUP_COMPLETE;
retval = count;
exit:
kfree(user_dev);
return retval;
}
static inline ssize_t uinput_inject_event(struct uinput_device *udev, const char __user *buffer, size_t count)
{
struct input_event ev;
if (count < input_event_size())
return -EINVAL;
if (input_event_from_user(buffer, &ev))
return -EFAULT;
input_event(udev->dev, ev.type, ev.code, ev.value);
return input_event_size();
}
static ssize_t uinput_write(struct file *file, const char __user *buffer, size_t count, loff_t *ppos)
{
struct uinput_device *udev = file->private_data;
int retval;
retval = mutex_lock_interruptible(&udev->mutex);
if (retval)
return retval;
retval = udev->state == UIST_CREATED ?
uinput_inject_event(udev, buffer, count) :
uinput_setup_device(udev, buffer, count);
mutex_unlock(&udev->mutex);
return retval;
}
static ssize_t uinput_read(struct file *file, char __user *buffer, size_t count, loff_t *ppos)
{
struct uinput_device *udev = file->private_data;
int retval = 0;
if (udev->state != UIST_CREATED)
return -ENODEV;
if (udev->head == udev->tail && (file->f_flags & O_NONBLOCK))
return -EAGAIN;
retval = wait_event_interruptible(udev->waitq,
udev->head != udev->tail || udev->state != UIST_CREATED);
if (retval)
return retval;
retval = mutex_lock_interruptible(&udev->mutex);
if (retval)
return retval;
if (udev->state != UIST_CREATED) {
retval = -ENODEV;
goto out;
}
while (udev->head != udev->tail && retval + input_event_size() <= count) {
if (input_event_to_user(buffer + retval, &udev->buff[udev->tail])) {
retval = -EFAULT;
goto out;
}
udev->tail = (udev->tail + 1) % UINPUT_BUFFER_SIZE;
retval += input_event_size();
}
out:
mutex_unlock(&udev->mutex);
return retval;
}
static unsigned int uinput_poll(struct file *file, poll_table *wait)
{
struct uinput_device *udev = file->private_data;
poll_wait(file, &udev->waitq, wait);
if (udev->head != udev->tail)
return POLLIN | POLLRDNORM;
return 0;
}
static int uinput_release(struct inode *inode, struct file *file)
{
struct uinput_device *udev = file->private_data;
uinput_destroy_device(udev);
kfree(udev);
return 0;
}
#ifdef CONFIG_COMPAT
struct uinput_ff_upload_compat {
int request_id;
int retval;
struct ff_effect_compat effect;
struct ff_effect_compat old;
};
static int uinput_ff_upload_to_user(char __user *buffer,
const struct uinput_ff_upload *ff_up)
{
if (INPUT_COMPAT_TEST) {
struct uinput_ff_upload_compat ff_up_compat;
ff_up_compat.request_id = ff_up->request_id;
ff_up_compat.retval = ff_up->retval;
/*
* It so happens that the pointer that gives us the trouble
* is the last field in the structure. Since we don't support
* custom waveforms in uinput anyway we can just copy the whole
* thing (to the compat size) and ignore the pointer.
*/
memcpy(&ff_up_compat.effect, &ff_up->effect,
sizeof(struct ff_effect_compat));
memcpy(&ff_up_compat.old, &ff_up->old,
sizeof(struct ff_effect_compat));
if (copy_to_user(buffer, &ff_up_compat,
sizeof(struct uinput_ff_upload_compat)))
return -EFAULT;
} else {
if (copy_to_user(buffer, ff_up,
sizeof(struct uinput_ff_upload)))
return -EFAULT;
}
return 0;
}
static int uinput_ff_upload_from_user(const char __user *buffer,
struct uinput_ff_upload *ff_up)
{
if (INPUT_COMPAT_TEST) {
struct uinput_ff_upload_compat ff_up_compat;
if (copy_from_user(&ff_up_compat, buffer,
sizeof(struct uinput_ff_upload_compat)))
return -EFAULT;
ff_up->request_id = ff_up_compat.request_id;
ff_up->retval = ff_up_compat.retval;
memcpy(&ff_up->effect, &ff_up_compat.effect,
sizeof(struct ff_effect_compat));
memcpy(&ff_up->old, &ff_up_compat.old,
sizeof(struct ff_effect_compat));
} else {
if (copy_from_user(ff_up, buffer,
sizeof(struct uinput_ff_upload)))
return -EFAULT;
}
return 0;
}
#else
static int uinput_ff_upload_to_user(char __user *buffer,
const struct uinput_ff_upload *ff_up)
{
if (copy_to_user(buffer, ff_up, sizeof(struct uinput_ff_upload)))
return -EFAULT;
return 0;
}
static int uinput_ff_upload_from_user(const char __user *buffer,
struct uinput_ff_upload *ff_up)
{
if (copy_from_user(ff_up, buffer, sizeof(struct uinput_ff_upload)))
return -EFAULT;
return 0;
}
#endif
#define uinput_set_bit(_arg, _bit, _max) \
({ \
int __ret = 0; \
if (udev->state == UIST_CREATED) \
__ret = -EINVAL; \
else if ((_arg) > (_max)) \
__ret = -EINVAL; \
else set_bit((_arg), udev->dev->_bit); \
__ret; \
})
static long uinput_ioctl_handler(struct file *file, unsigned int cmd,
unsigned long arg, void __user *p)
{
int retval;
struct uinput_device *udev = file->private_data;
struct uinput_ff_upload ff_up;
struct uinput_ff_erase ff_erase;
struct uinput_request *req;
int length;
char *phys;
retval = mutex_lock_interruptible(&udev->mutex);
if (retval)
return retval;
if (!udev->dev) {
retval = uinput_allocate_device(udev);
if (retval)
goto out;
}
switch (cmd) {
case UI_DEV_CREATE:
retval = uinput_create_device(udev);
break;
case UI_DEV_DESTROY:
uinput_destroy_device(udev);
break;
case UI_SET_EVBIT:
retval = uinput_set_bit(arg, evbit, EV_MAX);
break;
case UI_SET_KEYBIT:
retval = uinput_set_bit(arg, keybit, KEY_MAX);
break;
case UI_SET_RELBIT:
retval = uinput_set_bit(arg, relbit, REL_MAX);
break;
case UI_SET_ABSBIT:
retval = uinput_set_bit(arg, absbit, ABS_MAX);
break;
case UI_SET_MSCBIT:
retval = uinput_set_bit(arg, mscbit, MSC_MAX);
break;
case UI_SET_LEDBIT:
retval = uinput_set_bit(arg, ledbit, LED_MAX);
break;
case UI_SET_SNDBIT:
retval = uinput_set_bit(arg, sndbit, SND_MAX);
break;
case UI_SET_FFBIT:
retval = uinput_set_bit(arg, ffbit, FF_MAX);
break;
case UI_SET_SWBIT:
retval = uinput_set_bit(arg, swbit, SW_MAX);
break;
case UI_SET_PHYS:
if (udev->state == UIST_CREATED) {
retval = -EINVAL;
goto out;
}
length = strnlen_user(p, 1024);
if (length <= 0) {
retval = -EFAULT;
break;
}
kfree(udev->dev->phys);
udev->dev->phys = phys = kmalloc(length, GFP_KERNEL);
if (!phys) {
retval = -ENOMEM;
break;
}
if (copy_from_user(phys, p, length)) {
udev->dev->phys = NULL;
kfree(phys);
retval = -EFAULT;
break;
}
phys[length - 1] = '\0';
break;
case UI_BEGIN_FF_UPLOAD:
retval = uinput_ff_upload_from_user(p, &ff_up);
if (retval)
break;
req = uinput_request_find(udev, ff_up.request_id);
if (!req || req->code != UI_FF_UPLOAD || !req->u.upload.effect) {
retval = -EINVAL;
break;
}
ff_up.retval = 0;
ff_up.effect = *req->u.upload.effect;
if (req->u.upload.old)
ff_up.old = *req->u.upload.old;
else
memset(&ff_up.old, 0, sizeof(struct ff_effect));
retval = uinput_ff_upload_to_user(p, &ff_up);
break;
case UI_BEGIN_FF_ERASE:
if (copy_from_user(&ff_erase, p, sizeof(ff_erase))) {
retval = -EFAULT;
break;
}
req = uinput_request_find(udev, ff_erase.request_id);
if (!req || req->code != UI_FF_ERASE) {
retval = -EINVAL;
break;
}
ff_erase.retval = 0;
ff_erase.effect_id = req->u.effect_id;
if (copy_to_user(p, &ff_erase, sizeof(ff_erase))) {
retval = -EFAULT;
break;
}
break;
case UI_END_FF_UPLOAD:
retval = uinput_ff_upload_from_user(p, &ff_up);
if (retval)
break;
req = uinput_request_find(udev, ff_up.request_id);
if (!req || req->code != UI_FF_UPLOAD ||
!req->u.upload.effect) {
retval = -EINVAL;
break;
}
req->retval = ff_up.retval;
uinput_request_done(udev, req);
break;
case UI_END_FF_ERASE:
if (copy_from_user(&ff_erase, p, sizeof(ff_erase))) {
retval = -EFAULT;
break;
}
req = uinput_request_find(udev, ff_erase.request_id);
if (!req || req->code != UI_FF_ERASE) {
retval = -EINVAL;
break;
}
req->retval = ff_erase.retval;
uinput_request_done(udev, req);
break;
default:
retval = -EINVAL;
}
out:
mutex_unlock(&udev->mutex);
return retval;
}
static long uinput_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
{
return uinput_ioctl_handler(file, cmd, arg, (void __user *)arg);
}
#ifdef CONFIG_COMPAT
static long uinput_compat_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
{
return uinput_ioctl_handler(file, cmd, arg, compat_ptr(arg));
}
#endif
static const struct file_operations uinput_fops = {
.owner = THIS_MODULE,
.open = uinput_open,
.release = uinput_release,
.read = uinput_read,
.write = uinput_write,
.poll = uinput_poll,
.unlocked_ioctl = uinput_ioctl,
#ifdef CONFIG_COMPAT
.compat_ioctl = uinput_compat_ioctl,
#endif
};
static struct miscdevice uinput_misc = {
.fops = &uinput_fops,
.minor = UINPUT_MINOR,
.name = UINPUT_NAME,
};
static int __init uinput_init(void)
{
return misc_register(&uinput_misc);
}
static void __exit uinput_exit(void)
{
misc_deregister(&uinput_misc);
}
MODULE_AUTHOR("Aristeu Sergio Rozanski Filho");
MODULE_DESCRIPTION("User level driver support for input subsystem");
MODULE_LICENSE("GPL");
MODULE_VERSION("0.3");
module_init(uinput_init);
module_exit(uinput_exit);

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,163 @@
/**
* wm831x-on.c - WM831X ON pin driver
*
* Copyright (C) 2009 Wolfson Microelectronics plc
*
* 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 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
*/
#include <linux/module.h>
#include <linux/init.h>
#include <linux/kernel.h>
#include <linux/errno.h>
#include <linux/input.h>
#include <linux/interrupt.h>
#include <linux/platform_device.h>
#include <linux/workqueue.h>
#include <linux/mfd/wm831x/core.h>
struct wm831x_on {
struct input_dev *dev;
struct delayed_work work;
struct wm831x *wm831x;
};
/*
* The chip gives us an interrupt when the ON pin is asserted but we
* then need to poll to see when the pin is deasserted.
*/
static void wm831x_poll_on(struct work_struct *work)
{
struct wm831x_on *wm831x_on = container_of(work, struct wm831x_on,
work.work);
struct wm831x *wm831x = wm831x_on->wm831x;
int poll, ret;
ret = wm831x_reg_read(wm831x, WM831X_ON_PIN_CONTROL);
if (ret >= 0) {
poll = !(ret & WM831X_ON_PIN_STS);
input_report_key(wm831x_on->dev, KEY_POWER, poll);
input_sync(wm831x_on->dev);
} else {
dev_err(wm831x->dev, "Failed to read ON status: %d\n", ret);
poll = 1;
}
if (poll)
schedule_delayed_work(&wm831x_on->work, 100);
}
static irqreturn_t wm831x_on_irq(int irq, void *data)
{
struct wm831x_on *wm831x_on = data;
schedule_delayed_work(&wm831x_on->work, 0);
return IRQ_HANDLED;
}
static int __devinit wm831x_on_probe(struct platform_device *pdev)
{
struct wm831x *wm831x = dev_get_drvdata(pdev->dev.parent);
struct wm831x_on *wm831x_on;
int irq = platform_get_irq(pdev, 0);
int ret;
wm831x_on = kzalloc(sizeof(struct wm831x_on), GFP_KERNEL);
if (!wm831x_on) {
dev_err(&pdev->dev, "Can't allocate data\n");
return -ENOMEM;
}
wm831x_on->wm831x = wm831x;
INIT_DELAYED_WORK(&wm831x_on->work, wm831x_poll_on);
wm831x_on->dev = input_allocate_device();
if (!wm831x_on->dev) {
dev_err(&pdev->dev, "Can't allocate input dev\n");
ret = -ENOMEM;
goto err;
}
wm831x_on->dev->evbit[0] = BIT_MASK(EV_KEY);
wm831x_on->dev->keybit[BIT_WORD(KEY_POWER)] = BIT_MASK(KEY_POWER);
wm831x_on->dev->name = "wm831x_on";
wm831x_on->dev->phys = "wm831x_on/input0";
wm831x_on->dev->dev.parent = &pdev->dev;
ret = wm831x_request_irq(wm831x, irq, wm831x_on_irq,
IRQF_TRIGGER_RISING, "wm831x_on", wm831x_on);
if (ret < 0) {
dev_err(&pdev->dev, "Unable to request IRQ: %d\n", ret);
goto err_input_dev;
}
ret = input_register_device(wm831x_on->dev);
if (ret) {
dev_dbg(&pdev->dev, "Can't register input device: %d\n", ret);
goto err_irq;
}
platform_set_drvdata(pdev, wm831x_on);
return 0;
err_irq:
wm831x_free_irq(wm831x, irq, NULL);
err_input_dev:
input_free_device(wm831x_on->dev);
err:
kfree(wm831x_on);
return ret;
}
static int __devexit wm831x_on_remove(struct platform_device *pdev)
{
struct wm831x_on *wm831x_on = platform_get_drvdata(pdev);
int irq = platform_get_irq(pdev, 0);
wm831x_free_irq(wm831x_on->wm831x, irq, wm831x_on);
cancel_delayed_work_sync(&wm831x_on->work);
input_unregister_device(wm831x_on->dev);
kfree(wm831x_on);
return 0;
}
static struct platform_driver wm831x_on_driver = {
.probe = wm831x_on_probe,
.remove = __devexit_p(wm831x_on_remove),
.driver = {
.name = "wm831x-on",
.owner = THIS_MODULE,
},
};
static int __init wm831x_on_init(void)
{
return platform_driver_register(&wm831x_on_driver);
}
module_init(wm831x_on_init);
static void __exit wm831x_on_exit(void)
{
platform_driver_unregister(&wm831x_on_driver);
}
module_exit(wm831x_on_exit);
MODULE_ALIAS("platform:wm831x-on");
MODULE_DESCRIPTION("WM831x ON pin");
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Mark Brown <broonie@opensource.wolfsonmicro.com>");

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,220 @@
/*
* drivers/usb/input/yealink.h
*
* Copyright (c) 2005 Henk Vergonet <Henk.Vergonet@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.
*
* 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
*/
#ifndef INPUT_YEALINK_H
#define INPUT_YEALINK_H
/* Using the control channel on interface 3 various aspects of the phone
* can be controlled like LCD, LED, dialtone and the ringtone.
*/
struct yld_ctl_packet {
u8 cmd; /* command code, see below */
u8 size; /* 1-11, size of used data bytes. */
u16 offset; /* internal packet offset */
u8 data[11];
s8 sum; /* negative sum of 15 preceding bytes */
} __attribute__ ((packed));
#define USB_PKT_LEN sizeof(struct yld_ctl_packet)
/* The following yld_ctl_packet's are available: */
/* Init registers
*
* cmd 0x8e
* size 10
* offset 0
* data 0,0,0,0....
*/
#define CMD_INIT 0x8e
/* Request key scan
*
* cmd 0x80
* size 1
* offset 0
* data[0] on return returns the key number, if it changes there's a new
* key pressed.
*/
#define CMD_KEYPRESS 0x80
/* Request scancode
*
* cmd 0x81
* size 1
* offset key number [0-1f]
* data[0] on return returns the scancode
*/
#define CMD_SCANCODE 0x81
/* Set LCD
*
* cmd 0x04
* size 1-11
* offset 0-23
* data segment bits
*/
#define CMD_LCD 0x04
/* Set led
*
* cmd 0x05
* size 1
* offset 0
* data[0] 0 OFF / 1 ON
*/
#define CMD_LED 0x05
/* Set ringtone volume
*
* cmd 0x11
* size 1
* offset 0
* data[0] 0-0xff volume
*/
#define CMD_RING_VOLUME 0x11
/* Set ringtone notes
*
* cmd 0x02
* size 1-11
* offset 0->
* data binary representation LE16(-freq), LE16(duration) ....
*/
#define CMD_RING_NOTE 0x02
/* Sound ringtone via the speaker on the back
*
* cmd 0x03
* size 1
* offset 0
* data[0] 0 OFF / 0x24 ON
*/
#define CMD_RINGTONE 0x03
/* Sound dial tone via the ear speaker
*
* cmd 0x09
* size 1
* offset 0
* data[0] 0 OFF / 1 ON
*/
#define CMD_DIALTONE 0x09
#endif /* INPUT_YEALINK_H */
#if defined(_SEG) && defined(_PIC)
/* This table maps the LCD segments onto individual bit positions in the
* yld_status struct.
*/
/* LCD, each segment must be driven seperately.
*
* Layout:
*
* |[] [][] [][] [][] in |[][]
* |[] M [][] D [][] : [][] out |[][]
* store
*
* NEW REP SU MO TU WE TH FR SA
*
* [] [] [] [] [] [] [] [] [] [] [] []
* [] [] [] [] [] [] [] [] [] [] [] []
*/
/* Line 1
* Format : 18.e8.M8.88...188
* Icon names : M D : IN OUT STORE
*/
#define LCD_LINE1_OFFSET 0
#define LCD_LINE1_SIZE 17
/* Note: first g then f => ! ! */
/* _SEG( type a b c d e g f ) */
_SEG('1', 0,0 , 22,2 , 22,2 , 0,0 , 0,0 , 0,0 , 0,0 ),
_SEG('8', 20,1 , 20,2 , 20,4 , 20,8 , 21,4 , 21,2 , 21,1 ),
_PIC('.', 22,1 , "M" ),
_SEG('e', 18,1 , 18,2 , 18,4 , 18,1 , 19,2 , 18,1 , 19,1 ),
_SEG('8', 16,1 , 16,2 , 16,4 , 16,8 , 17,4 , 17,2 , 17,1 ),
_PIC('.', 15,8 , "D" ),
_SEG('M', 14,1 , 14,2 , 14,4 , 14,1 , 15,4 , 15,2 , 15,1 ),
_SEG('8', 12,1 , 12,2 , 12,4 , 12,8 , 13,4 , 13,2 , 13,1 ),
_PIC('.', 11,8 , ":" ),
_SEG('8', 10,1 , 10,2 , 10,4 , 10,8 , 11,4 , 11,2 , 11,1 ),
_SEG('8', 8,1 , 8,2 , 8,4 , 8,8 , 9,4 , 9,2 , 9,1 ),
_PIC('.', 7,1 , "IN" ),
_PIC('.', 7,2 , "OUT" ),
_PIC('.', 7,4 , "STORE" ),
_SEG('1', 0,0 , 5,1 , 5,1 , 0,0 , 0,0 , 0,0 , 0,0 ),
_SEG('8', 4,1 , 4,2 , 4,4 , 4,8 , 5,8 , 5,4 , 5,2 ),
_SEG('8', 2,1 , 2,2 , 2,4 , 2,8 , 3,4 , 3,2 , 3,1 ),
/* Line 2
* Format : .........
* Pict. name : NEW REP SU MO TU WE TH FR SA
*/
#define LCD_LINE2_OFFSET LCD_LINE1_OFFSET + LCD_LINE1_SIZE
#define LCD_LINE2_SIZE 9
_PIC('.', 23,2 , "NEW" ),
_PIC('.', 23,4 , "REP" ),
_PIC('.', 1,8 , "SU" ),
_PIC('.', 1,4 , "MO" ),
_PIC('.', 1,2 , "TU" ),
_PIC('.', 1,1 , "WE" ),
_PIC('.', 0,1 , "TH" ),
_PIC('.', 0,2 , "FR" ),
_PIC('.', 0,4 , "SA" ),
/* Line 3
* Format : 888888888888
*/
#define LCD_LINE3_OFFSET LCD_LINE2_OFFSET + LCD_LINE2_SIZE
#define LCD_LINE3_SIZE 12
_SEG('8', 22,16, 22,32, 22,64, 22,128, 23,128, 23,64, 23,32 ),
_SEG('8', 20,16, 20,32, 20,64, 20,128, 21,128, 21,64, 21,32 ),
_SEG('8', 18,16, 18,32, 18,64, 18,128, 19,128, 19,64, 19,32 ),
_SEG('8', 16,16, 16,32, 16,64, 16,128, 17,128, 17,64, 17,32 ),
_SEG('8', 14,16, 14,32, 14,64, 14,128, 15,128, 15,64, 15,32 ),
_SEG('8', 12,16, 12,32, 12,64, 12,128, 13,128, 13,64, 13,32 ),
_SEG('8', 10,16, 10,32, 10,64, 10,128, 11,128, 11,64, 11,32 ),
_SEG('8', 8,16, 8,32, 8,64, 8,128, 9,128, 9,64, 9,32 ),
_SEG('8', 6,16, 6,32, 6,64, 6,128, 7,128, 7,64, 7,32 ),
_SEG('8', 4,16, 4,32, 4,64, 4,128, 5,128, 5,64, 5,32 ),
_SEG('8', 2,16, 2,32, 2,64, 2,128, 3,128, 3,64, 3,32 ),
_SEG('8', 0,16, 0,32, 0,64, 0,128, 1,128, 1,64, 1,32 ),
/* Line 4
*
* The LED, DIALTONE and RINGTONE are implemented as icons and use the same
* sysfs interface.
*/
#define LCD_LINE4_OFFSET LCD_LINE3_OFFSET + LCD_LINE3_SIZE
_PIC('.', offsetof(struct yld_status, led) , 0x01, "LED" ),
_PIC('.', offsetof(struct yld_status, dialtone) , 0x01, "DIALTONE" ),
_PIC('.', offsetof(struct yld_status, ringtone) , 0x24, "RINGTONE" ),
#undef _SEG
#undef _PIC
#endif /* _SEG && _PIC */