rendered paste body/*
* linux/drivers/mfd/ucb1x00-switches.c
*
* Copyright (C) 2007 Bernhard Guillon.
*
* 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.
*
* This driver is for the Switches of Siemens SIMpad (CL4,SL4,SLC), T-Sinus-Pad and
* Swisscom WP50 devices.
*
* Six switches are routed to GPIO pins on the UCB1300: S3 -- S8.
*
* This driver is based on the 2.4 ucb1x00-switches, the 2.6 ucb1x00-assabet
* and the ucb1x00-ts driver.
*
*/
#include <linux/module.h>
#include <linux/init.h>
#include <linux/input.h>
#include <linux/device.h>
#include <asm/dma.h>
#include "ucb1x00.h"
unsigned short int last;
struct ucb1x00_switches {
struct input_dev *idev;
struct ucb1x00 *ucb;
};
static void ucb1x00_dev_irq(int idx, void *id)
{
//TODO! replace the extern to static!
extern unsigned short int last;
unsigned short int this;
struct ucb1x00_switches *switches = id;
ucb1x00_enable(switches->ucb);
this=~ucb1x00_io_read(switches->ucb);
if (this==last) {
return;
}
last=this;
switch (idx) {
case 0:
if ((this & (1<<0)) != 0) {
input_report_key(switches->idev, KEY_PROG1, 1); /* keypress */
printk(KERN_DEBUG "Key press Button: 1 \n");
}
else {
input_report_key(switches->idev, KEY_PROG1, 0); /* release */
printk(KERN_DEBUG "Key release Button: 1 \n");
}
break;
case 1:
if ((this & (1<<1)) != 0) {
input_report_key(switches->idev, KEY_PROG2, 1); /* keypress */
printk(KERN_DEBUG "Key press Button: 2 \n");
}
else {
input_report_key(switches->idev, KEY_PROG2, 0); /* release */
printk(KERN_DEBUG "Key release Button: 2 \n");
}
break;
case 2:
if ((this & (1<<2)) != 0) {
input_report_key(switches->idev, KEY_UP, 1); /* keypress */
printk(KERN_DEBUG "Key press Button: UP \n");
}
else {
input_report_key(switches->idev, KEY_UP, 0); /* release */
printk(KERN_DEBUG "Key release Button: UP \n");
}
break;
case 3:
if ((this & (1<<3)) != 0) {
input_report_key(switches->idev, KEY_DOWN, 1); /* keypress */
printk(KERN_DEBUG "Key press Button: DOWN \n");
}
else {
input_report_key(switches->idev, KEY_DOWN, 0); /* release */
printk(KERN_DEBUG "Key release Button: DOWN \n");
}
break;
case 4:
if ((this & (1<<4)) != 0) {
input_report_key(switches->idev, KEY_LEFT, 1); /* keypress */
printk(KERN_DEBUG "Key press Button: LEFT \n");
}
else {
input_report_key(switches->idev, KEY_LEFT, 0); /* release */
printk(KERN_DEBUG "Key release Button: LEFT \n");
}
break;
case 5:
if ((this & (1<<5)) != 0) {
input_report_key(switches->idev, KEY_RIGHT, 1); /* keypress */
printk(KERN_DEBUG "Key press Button: RIGHT \n");
}
else {
input_report_key(switches->idev, KEY_RIGHT, 0); /* release */
printk(KERN_DEBUG "Key release Button: RIGHT \n");
}
break;
default:
printk(KERN_DEBUG "switches-ucb1x00 is BUGGY!!! \n");
}
ucb1x00_disable_irq(switches->ucb, UCB_IRQ_TSPX, UCB_FALLING);
}
static int ucb1x00_switches_add(struct ucb1x00_dev *dev)
{
struct ucb1x00_switches *switches;
struct input_dev *idev;
int err,i;
switches = kzalloc(sizeof(struct ucb1x00_switches), GFP_KERNEL);
idev = input_allocate_device();
if (!switches || !idev) {
err = -ENOMEM;
goto fail;
}
switches->ucb = dev->ucb;
switches->idev = idev;
idev->private = switches;
idev->name = "SIMpad Switches";
idev->id.product = switches->ucb->id;
idev->evbit[0] = BIT(EV_KEY) | BIT(EV_REP);
set_bit(KEY_PROG1, idev->evbit);
set_bit(KEY_PROG2, idev->evbit);
set_bit(KEY_UP, idev->evbit);
set_bit(KEY_DOWN, idev->evbit);
set_bit(KEY_LEFT, idev->evbit);
set_bit(KEY_RIGHT, idev->evbit);
err = input_register_device(idev);
if (err)
goto fail;
dev->priv = switches;
ucb1x00_enable(switches->ucb);
ucb1x00_io_set_dir(switches->ucb,
UCB_IO_0 | UCB_IO_1 | UCB_IO_2 |
UCB_IO_3 | UCB_IO_4 | UCB_IO_5,
UCB_IO_8 | UCB_IO_9);
ucb1x00_disable(switches->ucb);
for (i = 0; i < 6; ++i) {
ucb1x00_enable_irq(switches->ucb, i, UCB_RISING | UCB_FALLING);
if (ucb1x00_hook_irq(switches->ucb, i, ucb1x00_dev_irq, switches) < 0) {
printk(KERN_ERR "unable to hook IRQ for "
"UCB1300 SWITCH_%d\n", i);
return -EBUSY;
}
}
return 0;
fail:
input_free_device(idev);
kfree(switches);
return err;
}
static void ucb1x00_switches_remove(struct ucb1x00_dev *dev)
{
struct ucb1x00_switches *switches = dev->priv;
input_unregister_device(switches->idev);
kfree(switches);
}
static struct ucb1x00_driver ucb1x00_switches_driver = {
.add = ucb1x00_switches_add,
.remove = ucb1x00_switches_remove,
};
static int __init ucb1x00_switches_init(void)
{
return ucb1x00_register_driver(&ucb1x00_switches_driver);
}
static void __exit ucb1x00_switches_exit(void)
{
ucb1x00_unregister_driver(&ucb1x00_switches_driver);
}
module_init(ucb1x00_switches_init);
module_exit(ucb1x00_switches_exit);
MODULE_AUTHOR("Bernhard Guillon <Bernhard.Guillon@opensimpad.org>");
MODULE_DESCRIPTION("UCB1x00 Switches driver for Siemens SIMpad");
MODULE_LICENSE("GPL");