1 /* 2 * $Id: pc110pad.c,v 1.12 2001/09/25 10:12:07 vojtech Exp $ 3 * 4 * Copyright (c) 2000-2001 Vojtech Pavlik 5 * 6 * Based on the work of: 7 * Alan Cox Robin O'Leary 8 */ 9 10 /* 11 * IBM PC110 touchpad driver for Linux 12 */ 13 14 /* 15 * This program is free software; you can redistribute it and/or modify 16 * it under the terms of the GNU General Public License as published by 17 * the Free Software Foundation; either version 2 of the License, or 18 * (at your option) any later version. 19 * 20 * This program is distributed in the hope that it will be useful, 21 * but WITHOUT ANY WARRANTY; without even the implied warranty of 22 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 23 * GNU General Public License for more details. 24 * 25 * You should have received a copy of the GNU General Public License 26 * along with this program; if not, write to the Free Software 27 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 28 * 29 * Should you need to contact me, the author, you can do so either by 30 * e-mail - mail your message to <vojtech@ucw.cz>, or by paper mail: 31 * Vojtech Pavlik, Simunkova 1594, Prague 8, 182 00 Czech Republic 32 */ 33 34 #include <linux/module.h> 35 #include <linux/kernel.h> 36 #include <linux/errno.h> 37 #include <linux/ioport.h> 38 #include <linux/input.h> 39 #include <linux/init.h> 40 #include <linux/interrupt.h> 41 #include <linux/pci.h> 42 43 #include <asm/io.h> 44 #include <asm/irq.h> 45 46 MODULE_AUTHOR("Vojtech Pavlik <vojtech@ucw.cz>"); 47 MODULE_DESCRIPTION("IBM PC110 touchpad driver"); 48 MODULE_LICENSE("GPL"); 49 50 #define PC110PAD_OFF 0x30 51 #define PC110PAD_ON 0x38 52 53 static int pc110pad_irq = 10; 54 static int pc110pad_io = 0x15e0; 55 56 static struct input_dev *pc110pad_dev; 57 static int pc110pad_data[3]; 58 static int pc110pad_count; 59 60 static irqreturn_t pc110pad_interrupt(int irq, void *ptr) 61 { 62 int value = inb_p(pc110pad_io); 63 int handshake = inb_p(pc110pad_io + 2); 64 65 outb_p(handshake | 1, pc110pad_io + 2); 66 outb_p(handshake & ~1, pc110pad_io + 2); 67 inb_p(0x64); 68 69 pc110pad_data[pc110pad_count++] = value; 70 71 if (pc110pad_count < 3) 72 return IRQ_HANDLED; 73 74 input_report_key(pc110pad_dev, BTN_TOUCH, 75 pc110pad_data[0] & 0x01); 76 input_report_abs(pc110pad_dev, ABS_X, 77 pc110pad_data[1] | ((pc110pad_data[0] << 3) & 0x80) | ((pc110pad_data[0] << 1) & 0x100)); 78 input_report_abs(pc110pad_dev, ABS_Y, 79 pc110pad_data[2] | ((pc110pad_data[0] << 4) & 0x80)); 80 input_sync(pc110pad_dev); 81 82 pc110pad_count = 0; 83 return IRQ_HANDLED; 84 } 85 86 static void pc110pad_close(struct input_dev *dev) 87 { 88 outb(PC110PAD_OFF, pc110pad_io + 2); 89 } 90 91 static int pc110pad_open(struct input_dev *dev) 92 { 93 pc110pad_interrupt(0, NULL); 94 pc110pad_interrupt(0, NULL); 95 pc110pad_interrupt(0, NULL); 96 outb(PC110PAD_ON, pc110pad_io + 2); 97 pc110pad_count = 0; 98 99 return 0; 100 } 101 102 /* 103 * We try to avoid enabling the hardware if it's not 104 * there, but we don't know how to test. But we do know 105 * that the PC110 is not a PCI system. So if we find any 106 * PCI devices in the machine, we don't have a PC110. 107 */ 108 static int __init pc110pad_init(void) 109 { 110 struct pci_dev *dev; 111 int err; 112 113 dev = pci_get_device(PCI_ANY_ID, PCI_ANY_ID, NULL); 114 if (dev) { 115 pci_dev_put(dev); 116 return -ENODEV; 117 } 118 119 if (!request_region(pc110pad_io, 4, "pc110pad")) { 120 printk(KERN_ERR "pc110pad: I/O area %#x-%#x in use.\n", 121 pc110pad_io, pc110pad_io + 4); 122 return -EBUSY; 123 } 124 125 outb(PC110PAD_OFF, pc110pad_io + 2); 126 127 if (request_irq(pc110pad_irq, pc110pad_interrupt, 0, "pc110pad", NULL)) { 128 printk(KERN_ERR "pc110pad: Unable to get irq %d.\n", pc110pad_irq); 129 err = -EBUSY; 130 goto err_release_region; 131 } 132 133 pc110pad_dev = input_allocate_device(); 134 if (!pc110pad_dev) { 135 printk(KERN_ERR "pc110pad: Not enough memory.\n"); 136 err = -ENOMEM; 137 goto err_free_irq; 138 } 139 140 pc110pad_dev->name = "IBM PC110 TouchPad"; 141 pc110pad_dev->phys = "isa15e0/input0"; 142 pc110pad_dev->id.bustype = BUS_ISA; 143 pc110pad_dev->id.vendor = 0x0003; 144 pc110pad_dev->id.product = 0x0001; 145 pc110pad_dev->id.version = 0x0100; 146 147 pc110pad_dev->evbit[0] = BIT(EV_KEY) | BIT(EV_ABS); 148 pc110pad_dev->absbit[0] = BIT(ABS_X) | BIT(ABS_Y); 149 pc110pad_dev->keybit[LONG(BTN_TOUCH)] = BIT(BTN_TOUCH); 150 151 pc110pad_dev->absmax[ABS_X] = 0x1ff; 152 pc110pad_dev->absmax[ABS_Y] = 0x0ff; 153 154 pc110pad_dev->open = pc110pad_open; 155 pc110pad_dev->close = pc110pad_close; 156 157 err = input_register_device(pc110pad_dev); 158 if (err) 159 goto err_free_dev; 160 161 return 0; 162 163 err_free_dev: 164 input_free_device(pc110pad_dev); 165 err_free_irq: 166 free_irq(pc110pad_irq, NULL); 167 err_release_region: 168 release_region(pc110pad_io, 4); 169 170 return err; 171 } 172 173 static void __exit pc110pad_exit(void) 174 { 175 outb(PC110PAD_OFF, pc110pad_io + 2); 176 free_irq(pc110pad_irq, NULL); 177 input_unregister_device(pc110pad_dev); 178 release_region(pc110pad_io, 4); 179 } 180 181 module_init(pc110pad_init); 182 module_exit(pc110pad_exit); 183