Loading...
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 | /* obmouse.c -- HP omnibook 600C/CT pop-up mouse driver
*
* Copyright (C) 1999 Olivier Florent
* Copyright (C) 1999 Chuck Slivkoff <chuck_slivkoff_AT_hp.com>
* Copyright (C) 1999-2004 Grant Grundler <grundler_at_parisc-linux.org>
*
* OB600C/CT mouse can be compared to a tablet, as absolute coordinates
* are given by the hardware. This driver emulates a basic serial mouse
* protocol by translating absolute coordinates to relative moves.
* This works with gpm -t pnp and Xfree86 as a standard Micro$oft mouse.
*
* FIXME: This driver lacks a detection routine.
* i.e you must know that you have a 600C/CT before using it.
*
*
* 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
*
*
* 0.7 January 2004 grant
* convert to /dev/input
*
* 0.6 2 January 2004 grant
* converted to busmouse
*
* 0.5 27 december 2003 grant
* fix "built-in" code - added module_* calls
*
* 0.4 27 december 2002 grant grundler
* Add MODULES_ stuff (author, license, etc)
* fix read to return 0 if no change.
*
* 0.3 26 november 1999 grant grundler
* removed ifdefs for EMULATE_SERIAL_MOUSE and EMULATE_NCR_PEN.
* Only support "basic serial mouse protocol (gpm -t pnp)" which
* is also supported directly by Xfree.
*
* Moved delta(x,y) calculations into ob_interrupt.
* If we only get interrupts when the mouse moves,
* then only need to update delta(x,y) then too.
*
* 0.2 22 november 1999, grant grundler
* "man 4 mouse" explains really well how PNP mouse works. Read it.
*
* Fixed algorithm which generated delta(x,y) from current-last
* position reported. Now handles "roll-over" properly and
* speed scales _inversely_ (ie bigger number is slower).
* "speed" parameter can be set with "insmod obmouse speed=5" (default).
*
* 0.1a 16 november 1999, charles_slivkoff_at_hp.com
* File did not compile as received.
* Modified ob_write,ob_read parameters to match 2.2.x kernel.
* Added uaccess.h. Tried to fix ob_interrupt.
*
* 0.1 17 february 1999, olivier_florent_AT_hp.com
* original author.
*/
#include <linux/module.h>
#include <linux/errno.h>
#include <linux/kmod.h>
#include <linux/input.h>
#include <linux/kernel.h>
#include <linux/ioport.h>
#include <asm/io.h>
#include <asm/irq.h>
#undef OBMOUSE_DEBUG /* define to enable lots of printf */
#define OBMOUSE_NAME "HP omnibook 600C/CT pop-up mouse"
#define OBMOUSE_DEV_NAME "obmouse"
#define OBMOUSE_VERSION "v0.7"
/*
** "speed" of the mouse. Determines how "fast" the mouse "moves".
** Similar (but not the same) as acceleration. Using slower acceleration
** together with lower speed value might result in better mouse control.
** I've only used the default acceleration.
*/
int speed = 4; /* my personal preference */
/*
** OB 600 mouse doesn't completely act like a tablet.
** The xy-coordinates do in fact roll over which would
** not happen on a tablet.
*/
#define OB_ROLL_LIMIT 0xd00
/*
** obmouse HW specific data
*/
#define OBMOUSE_BASE (0x238) /* base address of the hardware */
#define OBMOUSE_EXTENT (3) /* from 0x238 to 0x23b */
#define OBMOUSE_IRQ (12) /* irq 12 */
/* The 4 high bits of OBMOUSE_INTR_CTL */
#define OBMOUSE_BTN_IRQ_MASK (0x10) /* WR: enable/disable button irq */
#define OBMOUSE_MOV_IRQ_MASK (0x20) /* WR: enable/disable movement irq */
#define OBMOUSE_BUTTON1_MASK (0x40) /* RD: status of button 1 */
#define OBMOUSE_BUTTON2_MASK (0x80) /* RD: status of button 2 */
#define OBMOUSE_COORD_ONLY(v) ((v) & 0xfff) /* ignore high 4 bits */
#define OBMOUSE_INTR_CTL (OBMOUSE_BASE+3)
#define OBMOUSE_INTR_BITS (OBMOUSE_BTN_IRQ_MASK | OBMOUSE_MOV_IRQ_MASK)
/* Enable/disable both button and movement interrupt */
#define OBMOUSE_ENABLE_INTR() outb(OBMOUSE_INTR_BITS, OBMOUSE_INTR_CTL)
#define OBMOUSE_DISABLE_INTR() outb(~OBMOUSE_INTR_BITS, OBMOUSE_INTR_CTL)
/* Amount mouse has to move before the hardware launchs a new
** interrupt (I think). 0x08 is the standard value.
** Write value to OBMOUSE_BASE each time an interrupt is handled to
** enable the next one.
*/
#define OBMOUSE_SENSITIVITY (0x08)
/* reset the sensitivity to enable next interrupt */
#define OBMOUSE_ENABLE_SENSE() outb(OBMOUSE_SENSITIVITY,OBMOUSE_BASE)
#define OBMOUSE_DISABLE_SENSE() outb(0,OBMOUSE_BASE)
static unsigned short lastx; /* last reported normalized coords */
static unsigned short lasty;
static unsigned char ob_opened = 0; /* 0=closed, 1=opened */
static struct input_dev obdev;
/*
** Omnibook 600 mouse ISR.
** Read the HW state and resets "SENSITIVITY" in order to re-arm
** the interrupt.
*/
static void
ob_interrupt(int irq, void *dev_id, struct pt_regs *regs)
{
short dx, dy;
unsigned short rawx, rawy;
rawx = inb(OBMOUSE_BASE+0) + (inb(OBMOUSE_BASE+1) << 8);
rawy = inb(OBMOUSE_BASE+2) + (inb(OBMOUSE_BASE+3) << 8);
#ifdef OBMOUSE_DEBUG
/* This printk is really useful for learning how the mouse actually behaves. */
printk("ob_intr: %4x,%4x\n", rawx, rawy);
#endif
/* reset the sensitivity */
OBMOUSE_ENABLE_SENSE();
/* ------------------------------------
** update delta(x,y) values.
** ------------------------------------
*/
dx = (short) rawx - (short) lastx ;
lastx = rawx;
{
register unsigned short coordY = OBMOUSE_COORD_ONLY(rawy);
dy = (short) coordY - (short) lasty ;
lasty = coordY;
}
/*
** determine if the reading "rolled" over.
** Not fool-proof but should be good enough.
*/
if (dx > OB_ROLL_LIMIT) {
/* 0xf80 - 0x80 = 0xf00 (and we want 0x100) */
dx = 0x1000 - dx;
} else if (-dx > OB_ROLL_LIMIT) {
/*
** 0x80 - 0xf80 = -0xf00 (and we want -0x100)
** -0x1000 - (-0xf00) = -0x1000 + 0xf00 = -0x100
*/
dx = -0x1000 - dx;
}
/* Same story with the Y-coordinate */
if ( dy > OB_ROLL_LIMIT) dy = 0x1000 - dy;
else if (-dy > OB_ROLL_LIMIT) dy = -0x1000 - dy;
dx /= speed; /* scale */
dy /= speed;
rawy = ~rawy; /* invert mouse buttons */
input_report_key(&obdev, BTN_LEFT, (rawy & 0x8000));
input_report_key(&obdev, BTN_RIGHT, (rawy & 0x4000));
/* obmouse acts like a table *EXCEPT* for the "roll-over". */
if (dx) input_report_rel(&obdev, REL_X, -dx);
if (dy) input_report_rel(&obdev, REL_Y, dy);
}
static int ob_open(struct input_dev *dev)
{
/* device is already opened */
if (ob_opened)
return -EBUSY;
#ifdef OBMOUSE_DEBUG
printk(OBMOUSE_DEV_NAME ": attempt request irq %d\n",OBMOUSE_IRQ);
#endif
/* Try to get the interrupt */
if (request_irq(OBMOUSE_IRQ,ob_interrupt,0,OBMOUSE_DEV_NAME,NULL)) {
printk (OBMOUSE_DEV_NAME ": request_irq failed for %d\n",OBMOUSE_IRQ);
return -EBUSY;
}
#ifdef OBMOUSE_DEBUG
printk(OBMOUSE_DEV_NAME ": irq %d registered\n",OBMOUSE_IRQ);
#endif
OBMOUSE_ENABLE_INTR() ;
OBMOUSE_ENABLE_SENSE() ;
MOD_INC_USE_COUNT;
ob_opened = 1 ;
return 0;
}
void ob_close(struct input_dev *dev)
{
/* device has never been opened */
if (!ob_opened)
return;
OBMOUSE_DISABLE_INTR() ;
free_irq(OBMOUSE_IRQ, NULL);
MOD_DEC_USE_COUNT;
ob_opened = 0 ;
}
#ifndef MODULE
static int __init obmouse_setup(char *str)
{
int ints[4];
str = get_options(str, ARRAY_SIZE(ints), ints);
if (ints[0] > 0)
speed=ints[1];
return 1;
}
__setup("speed=", obmouse_setup);
#endif /* !MODULE */
static int obmouse_init(void)
{
/* Get the IO Port region first */
if (request_region(OBMOUSE_BASE,OBMOUSE_EXTENT,OBMOUSE_DEV_NAME) < 0) {
printk(KERN_ERR OBMOUSE_DEV_NAME ": IO Port 0x%d not available!\n", OBMOUSE_BASE);
return -ENODEV;
}
OBMOUSE_DISABLE_INTR() ;
OBMOUSE_DISABLE_SENSE() ;
memset(&obdev, 0, sizeof(obdev));
obdev.name = OBMOUSE_NAME " " OBMOUSE_VERSION;
obdev.idbus = BUS_ISA;
obdev.idvendor = 0x103c; /* PCI_VENDOR_ID_HP */
obdev.idproduct = 0x0001;
obdev.idversion = 0x0100;
obdev.evbit[0] = BIT(EV_KEY) | BIT(EV_REL);
obdev.keybit[LONG(BTN_LEFT)] = BIT(BTN_LEFT) | BIT(BTN_RIGHT);
obdev.relbit[0] = BIT(REL_X) | BIT(REL_Y);
obdev.open = ob_open;
obdev.close = ob_close;
input_register_device(&obdev);
printk( OBMOUSE_NAME " " OBMOUSE_VERSION
" (0x%x, IRQ %d), Grant Grundler\n", OBMOUSE_BASE, OBMOUSE_IRQ);
return 0;
}
static void obmouse_exit(void)
{
OBMOUSE_DISABLE_INTR() ;
OBMOUSE_DISABLE_SENSE() ;
input_unregister_device(&obdev);
release_region(OBMOUSE_BASE,OBMOUSE_EXTENT);
printk(OBMOUSE_DEV_NAME ": closed\n");
}
module_init(obmouse_init);
module_exit(obmouse_exit);
EXPORT_NO_SYMBOLS;
MODULE_AUTHOR("Grant Grundler <grundler_at_parisc-linux.org>");
MODULE_DESCRIPTION(OBMOUSE_NAME);
MODULE_PARM(speed, "i");
MODULE_PARM_DESC(speed, "obmouse speed (not accel) control");
MODULE_LICENSE("GPL");
|