/* * 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 #include #include #include #include #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 "); MODULE_DESCRIPTION("UCB1x00 Switches driver for Siemens SIMpad"); MODULE_LICENSE("GPL");