2 * Copyright (c) 1998-2000 Apple Computer, Inc. All rights reserved.
4 * @APPLE_LICENSE_HEADER_START@
6 * The contents of this file constitute Original Code as defined in and
7 * are subject to the Apple Public Source License Version 1.1 (the
8 * "License"). You may not use this file except in compliance with the
9 * License. Please obtain a copy of the License at
10 * http://www.apple.com/publicsource and read it before using this file.
12 * This Original Code and all software distributed under the License are
13 * distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, EITHER
14 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
15 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
16 * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT. Please see the
17 * License for the specific language governing rights and limitations
20 * @APPLE_LICENSE_HEADER_END@
23 * 17 July 1998 sdouglas
24 * 22 Dec 2000 bubba - save global acceleration state when device is unplugged.
27 #include <IOKit/IOLib.h>
28 #include <IOKit/assert.h>
29 #include <IOKit/hidsystem/IOHIPointing.h>
30 #include <IOKit/hidsystem/IOHIDParameter.h>
31 #include <IOKit/pwr_mgt/RootDomain.h>
35 #define abs(_a) ((_a >= 0) ? _a : -_a)
38 #define super IOHIDevice
40 // Global variable glob_accel won't get clobbered by sleep/wake code,
41 // which will re-init this driver. The Window Server does not re-send
42 // the original acceleration value, so we need to restore it on unplugs
45 IOFixed glob_accel
= 0x8000;
47 bool HIPointinghasRoot( OSObject
* us
, void *, IOService
* yourDevice
);
49 OSDefineMetaClassAndStructors(IOHIPointing
, IOHIDevice
);
51 bool IOHIPointing::init(OSDictionary
* properties
)
53 if (!super::init(properties
)) return false;
56 * Initialize minimal state.
62 _convertAbsoluteToRelative
= false;
63 _contactToMove
= false;
65 _pressureThresholdToClick
= 128;
66 _previousLocation
.x
= 0;
67 _previousLocation
.y
= 0;
70 _deviceLock
= IOLockAlloc();
72 if (!_deviceLock
) return false;
74 IOLockInit(_deviceLock
);
79 bool IOHIPointing::start(IOService
* provider
)
81 if (!super::start(provider
)) return false;
84 * IOHIPointing serves both as a service and a nub (we lead a double
85 * life). Register ourselves as a nub to kick off matching.
89 addNotification( gIOPublishNotification
,serviceMatching("IOPMrootDomain"),
90 (IOServiceNotificationHandler
)HIPointinghasRoot
, this, 0 );
95 /* Here are some power management functions so we can tell when system is
96 going to sleep. We need to remember the acceleration value */
97 bool HIPointinghasRoot( OSObject
* us
, void *, IOService
* yourDevice
)
99 if (( yourDevice
!= NULL
) && ((IOHIPointing
*)us
)->_rootDomain
== 0)
101 ((IOHIPointing
*)us
)->_rootDomain
= yourDevice
;
102 ((IOPMrootDomain
*)yourDevice
)->registerInterestedDriver((IOService
*) us
);
107 IOReturn
IOHIPointing::powerStateWillChangeTo ( IOPMPowerFlags theFlags
, unsigned long unused1
,
110 if ( ! (theFlags
& IOPMPowerOn
) )
112 glob_accel
= _acceleration
; //Save old value before driver is torn down
114 return IOPMAckImplied
;
117 IOReturn
IOHIPointing::powerStateDidChangeTo ( IOPMPowerFlags theFlags
, unsigned long unused1
,
120 if (theFlags
& IOPMPowerOn
)
122 if (glob_accel
> 0x10000) //Just in case saved value is out of bounds
123 glob_accel
= 0x10000;
124 setupForAcceleration(glob_accel
);
127 return IOPMAckImplied
;
132 void IOHIPointing::free()
133 // Description: Go Away. Be careful when freeing the lock.
135 glob_accel
= _acceleration
;
136 // IOLog("***free -- glob_accel = %08lx\n", glob_accel );
142 IOTakeLock(_deviceLock
);
152 _rootDomain
->deRegisterInterestedDriver((IOService
*) this);
158 bool IOHIPointing::open(IOService
* client
,
159 IOOptionBits options
,
160 RelativePointerEventAction rpeAction
,
161 AbsolutePointerEventAction apeAction
,
162 ScrollWheelEventAction sweAction
)
164 // IOLog("***open -- glob_accel = %08lx\n", glob_accel );
165 if ( (-1 == _acceleration
) && (!resetPointer())) return false;
166 // IOLog("***open -- after reset is called, glob_accel = %08lx\n", glob_accel );
167 if (super::open(client
, options
))
169 // Note: client object is already retained by superclass' open()
170 _relativePointerEventTarget
= client
;
171 _relativePointerEventAction
= rpeAction
;
172 _absolutePointerEventTarget
= client
;
173 _absolutePointerEventAction
= apeAction
;
174 _scrollWheelEventTarget
= client
;
175 _scrollWheelEventAction
= sweAction
;
182 void IOHIPointing::close(IOService
* client
, IOOptionBits
)
184 glob_accel
= _acceleration
;
185 // IOLog("***close -- glob_accel = %08lx\n", glob_accel );
187 _relativePointerEventAction
= NULL
;
188 _relativePointerEventTarget
= 0;
189 _absolutePointerEventAction
= NULL
;
190 _absolutePointerEventTarget
= 0;
193 _rootDomain
->deRegisterInterestedDriver((IOService
*) this);
196 super::close(client
);
199 IOHIDKind
IOHIPointing::hidKind()
201 return kHIRelativePointingDevice
;
204 struct CursorDeviceSegment
{
209 typedef struct CursorDeviceSegment CursorDeviceSegment
;
211 void IOHIPointing::scalePointer(int * dxp
, int * dyp
)
212 // Description: Perform pointer acceleration computations here.
213 // Given the resolution, dx, dy, and time, compute the velocity
214 // of the pointer over a Manhatten distance in inches/second.
215 // Using this velocity, do a lookup in the pointerScaling table
216 // to select a scaling factor. Scale dx and dy up as appropriate.
218 // * _deviceLock should be held on entry
225 CursorDeviceSegment
* segment
;
232 absDx
= (dx
< 0) ? -dx
: dx
;
233 absDy
= (dy
< 0) ? -dy
: dy
;
236 mag
= (absDx
+ (absDy
/ 2));
238 mag
= (absDy
+ (absDx
/ 2));
245 segment
= (CursorDeviceSegment
*) _scaleSegments
;
246 mag
> segment
->devUnits
;
249 scale
= IOFixedDivide(
250 segment
->intercept
+ IOFixedMultiply( mag
, segment
->slope
),
253 dx
= IOFixedMultiply( dx
, scale
);
254 dy
= IOFixedMultiply( dy
, scale
);
263 // get fractional part with sign extend
265 _fractX
= dx
& 0xffff;
267 _fractX
= dx
| 0xffff0000;
269 _fractY
= dy
& 0xffff;
271 _fractY
= dy
| 0xffff0000;
276 This routine interpolates to find a point on the line [x1,y1] [x2,y2] which
277 is intersected by the line [x3,y3] [x3,y"]. The resulting y' is calculated
278 by interpolating between y3 and y", towards the higher acceleration curve.
281 static SInt32
Interpolate( SInt32 x1
, SInt32 y1
,
282 SInt32 x2
, SInt32 y2
,
283 SInt32 x3
, SInt32 y3
,
284 SInt32 scale
, Boolean lower
)
291 slope
= IOFixedDivide( y2
- y1
, x2
- x1
);
292 intercept
= y1
- IOFixedMultiply( slope
, x1
);
293 resultY
= intercept
+ IOFixedMultiply( slope
, x3
);
295 resultY
= y3
- IOFixedMultiply( scale
, y3
- resultY
);
297 resultY
= resultY
+ IOFixedMultiply( scale
, y3
- resultY
);
303 static SInt32
Fetch32( const UInt16
* p
)
307 result
= (*(p
++)) << 16;
313 void IOHIPointing::setupForAcceleration( IOFixed desired
)
316 const UInt16
* lowTable
= 0;
317 const UInt16
* highTable
;
319 SInt32 x1
, y1
, x2
, y2
, x3
, y3
;
320 SInt32 prevX1
, prevY1
;
321 SInt32 upperX
, upperY
;
322 SInt32 lowerX
, lowerY
;
323 SInt32 lowAccl
= 0, lowPoints
= 0;
324 SInt32 highAccl
, highPoints
;
329 SInt32 pointerResolution
= resolution();
330 SInt32 frameRate
= (67 << 16);
331 SInt32 screenResolution
= (72 << 16);
332 SInt32 devScale
, crsrScale
;
333 SInt32 scaledX1
, scaledY1
;
334 SInt32 scaledX2
, scaledY2
;
336 CursorDeviceSegment
* segments
;
337 CursorDeviceSegment
* segment
;
340 assert(pointerResolution
);
341 data
= copyAccelerationTable();
345 if( desired
< (IOFixed
) 0) {
346 // disabling mouse scaling
347 if(_scaleSegments
&& _scaleSegCount
)
348 IODelete( _scaleSegments
,
349 CursorDeviceSegment
, _scaleSegCount
);
350 _scaleSegments
= NULL
;
356 highTable
= (const UInt16
*) data
->getBytesNoCopy();
358 devScale
= IOFixedDivide( pointerResolution
, frameRate
);
359 crsrScale
= IOFixedDivide( screenResolution
, frameRate
);
361 scaledX1
= scaledY1
= 0;
363 scale
= Fetch32( highTable
);
366 _acceleration
= desired
;
368 // normalize table's default (scale) to 0.5
369 if( desired
> 0x8000) {
370 desired
= IOFixedMultiply( desired
- 0x8000,
375 desired
= IOFixedMultiply( desired
, scale
);
378 if( desired
> (1 << 16))
381 count
= *(highTable
++);
383 // find curves bracketing the desired value
385 highAccl
= Fetch32( highTable
);
387 highPoints
= *(highTable
++);
389 if( desired
<= highAccl
)
392 lowTable
= highTable
;
394 lowPoints
= highPoints
;
395 highTable
+= lowPoints
* 4;
399 // scale between the two
401 scale
= IOFixedDivide( desired
- lowAccl
,
402 highAccl
- lowAccl
);
403 // or take all the high one
406 lowTable
= highTable
;
411 if( lowPoints
> highPoints
)
412 segCount
= lowPoints
;
414 segCount
= highPoints
;
416 /* IOLog("lowPoints %ld, highPoints %ld, segCount %ld\n",
417 lowPoints, highPoints, segCount); */
418 segments
= IONew( CursorDeviceSegment
, segCount
);
422 x1
= prevX1
= y1
= prevY1
= 0;
424 lowerX
= Fetch32( lowTable
);
426 lowerY
= Fetch32( lowTable
);
428 upperX
= Fetch32( highTable
);
430 upperY
= Fetch32( highTable
);
434 // consume next point from first X
435 lower
= (lowPoints
&& (!highPoints
|| (lowerX
<= upperX
)));
443 if( lowPoints
&& (--lowPoints
)) {
444 lowerX
= Fetch32( lowTable
);
446 lowerY
= Fetch32( lowTable
);
455 if( highPoints
&& (--highPoints
)) {
456 upperX
= Fetch32( highTable
);
458 upperY
= Fetch32( highTable
);
463 // convert to line segment
464 assert( segment
< (segments
+ segCount
) );
466 scaledX2
= IOFixedMultiply( devScale
, /* newX */ x3
);
467 scaledY2
= IOFixedMultiply( crsrScale
,
468 /* newY */ Interpolate( x1
, y1
, x2
, y2
, x3
, y3
,
470 if( lowPoints
|| highPoints
)
471 segment
->devUnits
= scaledX2
;
473 segment
->devUnits
= 0x7fffffff;
474 segment
->slope
= IOFixedDivide( scaledY2
- scaledY1
,
475 scaledX2
- scaledX1
);
476 segment
->intercept
= scaledY2
477 - IOFixedMultiply( segment
->slope
, scaledX2
);
478 /* IOLog("devUnits = %08lx, slope = %08lx, intercept = %08lx\n",
479 segment->devUnits, segment->slope, segment->intercept); */
486 // continue on from last point
487 if( lowPoints
&& highPoints
) {
488 if( lowerX
> upperX
) {
507 } while( lowPoints
|| highPoints
);
509 if( _scaleSegCount
&& _scaleSegments
)
510 IODelete( _scaleSegments
,
511 CursorDeviceSegment
, _scaleSegCount
);
512 _scaleSegCount
= segCount
;
513 _scaleSegments
= (void *) segments
;
515 _fractX
= _fractY
= 0;
521 bool IOHIPointing::resetPointer()
523 IOTakeLock( _deviceLock
);
525 _buttonMode
= NX_RightButton
;
526 // IOLog("***resetPointer -- glob_accel = %08lx", glob_accel );
529 // Restore the last acceleration value, since we may have been hot
530 // unplugged and re-plugged.
531 setupForAcceleration(glob_accel
);
535 setupForAcceleration(0x8000);
539 IOUnlock( _deviceLock
);
543 void IOHIPointing::dispatchAbsolutePointerEvent(Point
* newLoc
,
556 IOTakeLock(_deviceLock
);
558 if (buttonState
& 1) {
562 if (buttonCount() > 1) {
563 if (buttonState
& -2) { // any other buttons
568 if ((_pressureThresholdToClick
< 255) && ((pressure
- pressureMin
) > ((pressureMax
- pressureMin
) * _pressureThresholdToClick
/ 256))) {
572 if (_buttonMode
== NX_OneButton
) {
573 if ((buttons
& (EV_LB
|EV_RB
)) != 0) {
578 if (_convertAbsoluteToRelative
) {
579 dx
= newLoc
->x
- _previousLocation
.x
;
580 dy
= newLoc
->y
- _previousLocation
.y
;
582 if ((_contactToMove
&& !_hadContact
&& (pressure
> pressureMin
)) || (abs(dx
) > ((bounds
->maxx
- bounds
->minx
) / 20)) || (abs(dy
) > ((bounds
->maxy
- bounds
->miny
) / 20))) {
586 scalePointer(&dx
, &dy
);
589 _previousLocation
.x
= newLoc
->x
;
590 _previousLocation
.y
= newLoc
->y
;
593 IOUnlock(_deviceLock
);
595 _hadContact
= (pressure
> pressureMin
);
597 if (!_contactToMove
|| (pressure
> pressureMin
)) {
598 pressure
-= pressureMin
;
599 if (pressure
> 255) {
602 if (_convertAbsoluteToRelative
) {
603 if (_relativePointerEventAction
&& _relativePointerEventTarget
) {
604 (*_relativePointerEventAction
)(_relativePointerEventTarget
,
611 if (_absolutePointerEventAction
&& _absolutePointerEventTarget
) {
612 (*_absolutePointerEventAction
)(_absolutePointerEventTarget
,
627 void IOHIPointing::dispatchRelativePointerEvent(int dx
,
634 IOTakeLock( _deviceLock
);
641 if( buttonCount() > 1) {
642 if( buttonState
& 2) // any others down
644 // Other magic bit reshuffling stuff. It seems there was space
645 // left over at some point for a "middle" mouse button between EV_LB and EV_RB
648 // Add in the rest of the buttons in a linear fasion...
649 buttons
|= buttonState
& ~0x7;
652 // Perform pointer acceleration computations
653 scalePointer(&dx
, &dy
);
655 // Perform button tying and mapping. This
656 // stuff applies to relative posn devices (mice) only.
657 if ( _buttonMode
== NX_OneButton
)
659 // Remap both Left and Right (but no others?) to Left.
660 if ( (buttons
& (EV_LB
|EV_RB
)) != 0 ) {
665 else if ( (buttonCount() > 1) && (_buttonMode
== NX_LeftButton
) )
666 // Menus on left button. Swap!
669 if ( buttons
& EV_LB
)
671 if ( buttons
& EV_RB
)
673 // Swap Left and Right, preserve everything else
674 buttons
= (buttons
& ~(EV_LB
|EV_RB
)) | temp
;
676 IOUnlock( _deviceLock
);
678 if (_relativePointerEventAction
) /* upstream call */
680 (*_relativePointerEventAction
)(_relativePointerEventTarget
,
681 /* buttons */ buttons
,
688 void IOHIPointing::dispatchScrollWheelEvent(short deltaAxis1
,
693 if (_scrollWheelEventAction
) {
694 (*_scrollWheelEventAction
)(_scrollWheelEventTarget
,
702 bool IOHIPointing::updateProperties( void )
705 UInt32 res
= resolution();
707 ok
= setProperty( kIOHIDPointerResolutionKey
, &res
, sizeof( res
))
708 & setProperty( kIOHIDPointerAccelerationKey
, &_acceleration
,
709 sizeof( _acceleration
))
710 & setProperty( kIOHIDPointerConvertAbsoluteKey
, &_convertAbsoluteToRelative
,
711 sizeof( _convertAbsoluteToRelative
))
712 & setProperty( kIOHIDPointerContactToMoveKey
, &_contactToMove
,
713 sizeof( _contactToMove
));
715 return( ok
& super::updateProperties() );
718 IOReturn
IOHIPointing::setParamProperties( OSDictionary
* dict
)
721 IOReturn err
= kIOReturnSuccess
;
722 bool updated
= false;
725 IOTakeLock( _deviceLock
);
726 if( (data
= OSDynamicCast( OSData
,
727 dict
->getObject(kIOHIDPointerAccelerationKey
)))) {
729 setupForAcceleration( *((IOFixed
*)data
->getBytesNoCopy()) );
732 IOUnlock( _deviceLock
);
734 if( dict
->getObject(kIOHIDResetPointerKey
))
737 if ((data
= OSDynamicCast(OSData
,
738 dict
->getObject(kIOHIDPointerConvertAbsoluteKey
)))) {
739 bytes
= (UInt8
*) data
->getBytesNoCopy();
740 _convertAbsoluteToRelative
= (bytes
[0] != 0) ? true : false;
744 if ((data
= OSDynamicCast(OSData
,
745 dict
->getObject(kIOHIDPointerContactToMoveKey
)))) {
746 bytes
= (UInt8
*) data
->getBytesNoCopy();
747 _contactToMove
= (bytes
[0] != 0) ? true : false;
757 // subclasses override
759 IOItemCount
IOHIPointing::buttonCount()
764 IOFixed
IOHIPointing::resolution()
769 OSData
* IOHIPointing::copyAccelerationTable()
771 static const UInt16 accl
[] = {
773 0x4032, 0x3030, 0x0002, 0x0000, 0x0000, 0x0001, 0x0001, 0x0000,
774 0x0001, 0x0000, 0x0001, 0x0000, 0x0009, 0x0000, 0x713B, 0x0000,
775 0x6000, 0x0004, 0x4EC5, 0x0010, 0x8000, 0x000C, 0x0000, 0x005F,
776 0x0000, 0x0016, 0xEC4F, 0x008B, 0x0000, 0x001D, 0x3B14, 0x0094,
777 0x8000, 0x0022, 0x7627, 0x0096, 0x0000, 0x0024, 0x6276, 0x0096,
778 0x0000, 0x0026, 0x0000, 0x0096, 0x0000, 0x0028, 0x0000, 0x0096,
782 OSData
* data
= OSDynamicCast( OSData
,
783 getProperty( "HIDPointerAccelerationTable" ));
787 data
= OSData::withBytesNoCopy( accl
, sizeof( accl
) );