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 static int pc110pad_used; 60 61 static char *pc110pad_name = "IBM PC110 TouchPad"; 62 static char *pc110pad_phys = "isa15e0/input0"; 63 64 static irqreturn_t pc110pad_interrupt(int irq, void *ptr, struct pt_regs *regs) 65 { 66 int value = inb_p(pc110pad_io); 67 int handshake = inb_p(pc110pad_io + 2); 68 69 outb_p(handshake | 1, pc110pad_io + 2); 70 outb_p(handshake & ~1, pc110pad_io + 2); 71 inb_p(0x64); 72 73 pc110pad_data[pc110pad_count++] = value; 74 75 if (pc110pad_count < 3) 76 return IRQ_HANDLED; 77 78 input_regs(&pc110pad_dev, regs); 79 input_report_key(&pc110pad_dev, BTN_TOUCH, 80 pc110pad_data[0] & 0x01); 81 input_report_abs(&pc110pad_dev, ABS_X, 82 pc110pad_data[1] | ((pc110pad_data[0] << 3) & 0x80) | ((pc110pad_data[0] << 1) & 0x100)); 83 input_report_abs(&pc110pad_dev, ABS_Y, 84 pc110pad_data[2] | ((pc110pad_data[0] << 4) & 0x80)); 85 input_sync(&pc110pad_dev); 86 87 pc110pad_count = 0; 88 return IRQ_HANDLED; 89 } 90 91 static void pc110pad_close(struct input_dev *dev) 92 { 93 if (!--pc110pad_used) 94 outb(PC110PAD_OFF, pc110pad_io + 2); 95 } 96 97 static int pc110pad_open(struct input_dev *dev) 98 { 99 if (pc110pad_used++) 100 return 0; 101 102 pc110pad_interrupt(0,NULL,NULL); 103 pc110pad_interrupt(0,NULL,NULL); 104 pc110pad_interrupt(0,NULL,NULL); 105 outb(PC110PAD_ON, pc110pad_io + 2); 106 pc110pad_count = 0; 107 108 return 0; 109 } 110 111 /* 112 * We try to avoid enabling the hardware if it's not 113 * there, but we don't know how to test. But we do know 114 * that the PC110 is not a PCI system. So if we find any 115 * PCI devices in the machine, we don't have a PC110. 116 */ 117 static int __init pc110pad_init(void) 118 { 119 struct pci_dev *dev; 120 121 dev = pci_get_device(PCI_ANY_ID, PCI_ANY_ID, NULL); 122 if (dev) { 123 pci_dev_put(dev); 124 return -ENOENT; 125 } 126 127 if (!request_region(pc110pad_io, 4, "pc110pad")) { 128 printk(KERN_ERR "pc110pad: I/O area %#x-%#x in use.\n", 129 pc110pad_io, pc110pad_io + 4); 130 return -EBUSY; 131 } 132 133 outb(PC110PAD_OFF, pc110pad_io + 2); 134 135 if (request_irq(pc110pad_irq, pc110pad_interrupt, 0, "pc110pad", NULL)) 136 { 137 release_region(pc110pad_io, 4); 138 printk(KERN_ERR "pc110pad: Unable to get irq %d.\n", pc110pad_irq); 139 return -EBUSY; 140 } 141 142 pc110pad_dev.evbit[0] = BIT(EV_KEY) | BIT(EV_ABS); 143 pc110pad_dev.absbit[0] = BIT(ABS_X) | BIT(ABS_Y); 144 pc110pad_dev.keybit[LONG(BTN_TOUCH)] = BIT(BTN_TOUCH); 145 146 pc110pad_dev.absmax[ABS_X] = 0x1ff; 147 pc110pad_dev.absmax[ABS_Y] = 0x0ff; 148 149 pc110pad_dev.open = pc110pad_open; 150 pc110pad_dev.close = pc110pad_close; 151 152 pc110pad_dev.name = pc110pad_name; 153 pc110pad_dev.phys = pc110pad_phys; 154 pc110pad_dev.id.bustype = BUS_ISA; 155 pc110pad_dev.id.vendor = 0x0003; 156 pc110pad_dev.id.product = 0x0001; 157 pc110pad_dev.id.version = 0x0100; 158 159 input_register_device(&pc110pad_dev); 160 161 printk(KERN_INFO "input: %s at %#x irq %d\n", 162 pc110pad_name, pc110pad_io, pc110pad_irq); 163 164 return 0; 165 } 166 167 static void __exit pc110pad_exit(void) 168 { 169 input_unregister_device(&pc110pad_dev); 170 171 outb(PC110PAD_OFF, pc110pad_io + 2); 172 173 free_irq(pc110pad_irq, NULL); 174 release_region(pc110pad_io, 4); 175 } 176 177 module_init(pc110pad_init); 178 module_exit(pc110pad_exit); 179