]> git.saurik.com Git - apple/xnu.git/blame - iokit/Families/IOHIDSystem/IOHIPointing.cpp
xnu-124.13.tar.gz
[apple/xnu.git] / iokit / Families / IOHIDSystem / IOHIPointing.cpp
CommitLineData
1c79356b
A
1/*
2 * Copyright (c) 1998-2000 Apple Computer, Inc. All rights reserved.
3 *
4 * @APPLE_LICENSE_HEADER_START@
5 *
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.
11 *
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
18 * under the License.
19 *
20 * @APPLE_LICENSE_HEADER_END@
21 */
22/*
23 * 17 July 1998 sdouglas
24 * 22 Dec 2000 bubba - save global acceleration state when device is unplugged.
25 */
26
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>
32
33
34#ifndef abs
35#define abs(_a) ((_a >= 0) ? _a : -_a)
36#endif
37
38#define super IOHIDevice
39
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
43// and sleeps.
44
45IOFixed glob_accel = 0x8000;
46
47bool HIPointinghasRoot( OSObject * us, void *, IOService * yourDevice );
48
49OSDefineMetaClassAndStructors(IOHIPointing, IOHIDevice);
50
51bool IOHIPointing::init(OSDictionary * properties)
52{
53 if (!super::init(properties)) return false;
54
55 /*
56 * Initialize minimal state.
57 */
58
59 _fractX = 0;
60 _fractY = 0;
61 _acceleration = -1;
62 _convertAbsoluteToRelative = false;
63 _contactToMove = false;
64 _hadContact = false;
65 _pressureThresholdToClick = 128;
66 _previousLocation.x = 0;
67 _previousLocation.y = 0;
68 _rootDomain = 0;
69
70 _deviceLock = IOLockAlloc();
71
72 if (!_deviceLock) return false;
73
74 IOLockInit(_deviceLock);
75
76 return true;
77}
78
79bool IOHIPointing::start(IOService * provider)
80{
81 if (!super::start(provider)) return false;
82
83 /*
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.
86 */
87
88 registerService();
89 addNotification( gIOPublishNotification,serviceMatching("IOPMrootDomain"),
90 (IOServiceNotificationHandler)HIPointinghasRoot, this, 0 );
91
92 return true;
93}
94
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 */
97bool HIPointinghasRoot( OSObject * us, void *, IOService * yourDevice )
98{
99 if (( yourDevice != NULL ) && ((IOHIPointing *)us)->_rootDomain == 0)
100 {
101 ((IOHIPointing *)us)->_rootDomain = yourDevice;
102 ((IOPMrootDomain *)yourDevice)->registerInterestedDriver((IOService *) us);
103 }
104 return true;
105}
106
107IOReturn IOHIPointing::powerStateWillChangeTo ( IOPMPowerFlags theFlags, unsigned long unused1,
108 IOService* unused2)
109{
110 if ( ! (theFlags & IOPMPowerOn) )
111 {
112 glob_accel = _acceleration; //Save old value before driver is torn down
113 }
114 return IOPMAckImplied;
115}
116
117IOReturn IOHIPointing::powerStateDidChangeTo ( IOPMPowerFlags theFlags, unsigned long unused1,
118 IOService* unused2)
119{
120 if (theFlags & IOPMPowerOn)
121 {
122 if (glob_accel > 0x10000) //Just in case saved value is out of bounds
123 glob_accel = 0x10000;
124 setupForAcceleration(glob_accel);
125 updateProperties();
126 }
127 return IOPMAckImplied;
128}
129
130
131
132void IOHIPointing::free()
133// Description: Go Away. Be careful when freeing the lock.
134{
135 glob_accel = _acceleration;
136// IOLog("***free -- glob_accel = %08lx\n", glob_accel );
137
138 if (_deviceLock)
139 {
140 IOLock * lock;
141
142 IOTakeLock(_deviceLock);
143
144 lock = _deviceLock;
145 _deviceLock = NULL;
146
147 IOUnlock(lock);
148 IOLockFree(lock);
149 }
150 if (_rootDomain)
151 {
152 _rootDomain->deRegisterInterestedDriver((IOService *) this);
153 _rootDomain = 0;
154 }
155 super::free();
156}
157
158bool IOHIPointing::open(IOService * client,
159 IOOptionBits options,
160 RelativePointerEventAction rpeAction,
161 AbsolutePointerEventAction apeAction,
162 ScrollWheelEventAction sweAction)
163{
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))
168 {
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;
176 return true;
177 }
178
179 return false;
180}
181
182void IOHIPointing::close(IOService * client, IOOptionBits)
183{
184 glob_accel = _acceleration;
185 // IOLog("***close -- glob_accel = %08lx\n", glob_accel );
186
187 _relativePointerEventAction = NULL;
188 _relativePointerEventTarget = 0;
189 _absolutePointerEventAction = NULL;
190 _absolutePointerEventTarget = 0;
191 if (_rootDomain)
192 {
193 _rootDomain->deRegisterInterestedDriver((IOService *) this);
194 _rootDomain = 0;
195 }
196 super::close(client);
197}
198
199IOHIDKind IOHIPointing::hidKind()
200{
201 return kHIRelativePointingDevice;
202}
203
204struct CursorDeviceSegment {
205 SInt32 devUnits;
206 SInt32 slope;
207 SInt32 intercept;
208};
209typedef struct CursorDeviceSegment CursorDeviceSegment;
210
211void 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.
217// Preconditions:
218// * _deviceLock should be held on entry
219{
220
221 SInt32 dx, dy;
222 SInt32 absDx, absDy;
223 SInt32 mag;
224 IOFixed scale;
225 CursorDeviceSegment * segment;
226
227 if( !_scaleSegments)
228 return;
229
230 dx = (*dxp) << 16;
231 dy = (*dyp) << 16;
232 absDx = (dx < 0) ? -dx : dx;
233 absDy = (dy < 0) ? -dy : dy;
234
235 if( absDx > absDy)
236 mag = (absDx + (absDy / 2));
237 else
238 mag = (absDy + (absDx / 2));
239
240 if( !mag)
241 return;
242
243 // scale
244 for(
245 segment = (CursorDeviceSegment *) _scaleSegments;
246 mag > segment->devUnits;
247 segment++) {}
248
249 scale = IOFixedDivide(
250 segment->intercept + IOFixedMultiply( mag, segment->slope ),
251 mag );
252
253 dx = IOFixedMultiply( dx, scale );
254 dy = IOFixedMultiply( dy, scale );
255
256 // add fract parts
257 dx += _fractX;
258 dy += _fractY;
259
260 *dxp = dx / 65536;
261 *dyp = dy / 65536;
262
263 // get fractional part with sign extend
264 if( dx >= 0)
265 _fractX = dx & 0xffff;
266 else
267 _fractX = dx | 0xffff0000;
268 if( dy >= 0)
269 _fractY = dy & 0xffff;
270 else
271 _fractY = dy | 0xffff0000;
272}
273
274/*
275 Routine: Interpolate
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.
279*/
280
281static SInt32 Interpolate( SInt32 x1, SInt32 y1,
282 SInt32 x2, SInt32 y2,
283 SInt32 x3, SInt32 y3,
284 SInt32 scale, Boolean lower )
285{
286
287 SInt32 slope;
288 SInt32 intercept;
289 SInt32 resultY;
290
291 slope = IOFixedDivide( y2 - y1, x2 - x1 );
292 intercept = y1 - IOFixedMultiply( slope, x1 );
293 resultY = intercept + IOFixedMultiply( slope, x3 );
294 if( lower)
295 resultY = y3 - IOFixedMultiply( scale, y3 - resultY );
296 else
297 resultY = resultY + IOFixedMultiply( scale, y3 - resultY );
298
299 return( resultY );
300}
301
302
303static SInt32 Fetch32( const UInt16 * p )
304{
305 SInt32 result;
306
307 result = (*(p++)) << 16;
308 result |= (*(p++));
309
310 return( result );
311}
312
313void IOHIPointing::setupForAcceleration( IOFixed desired )
314{
315 OSData * data;
316 const UInt16 * lowTable = 0;
317 const UInt16 * highTable;
318
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;
325 SInt32 scale;
326 UInt32 count;
327 Boolean lower;
328
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;
335
336 CursorDeviceSegment * segments;
337 CursorDeviceSegment * segment;
338 SInt32 segCount;
339
340 assert(pointerResolution);
341 data = copyAccelerationTable();
342 if( !data)
343 return;
344
345 if( desired < (IOFixed) 0) {
346 // disabling mouse scaling
347 if(_scaleSegments && _scaleSegCount)
348 IODelete( _scaleSegments,
349 CursorDeviceSegment, _scaleSegCount );
350 _scaleSegments = NULL;
351 _scaleSegCount = 0;
352 data->release();
353 return;
354 }
355
356 highTable = (const UInt16 *) data->getBytesNoCopy();
357
358 devScale = IOFixedDivide( pointerResolution, frameRate );
359 crsrScale = IOFixedDivide( screenResolution, frameRate );
360
361 scaledX1 = scaledY1 = 0;
362
363 scale = Fetch32( highTable );
364 highTable += 4;
365
366 _acceleration = desired;
367
368 // normalize table's default (scale) to 0.5
369 if( desired > 0x8000) {
370 desired = IOFixedMultiply( desired - 0x8000,
371 0x10000 - scale );
372 desired <<= 1;
373 desired += scale;
374 } else {
375 desired = IOFixedMultiply( desired, scale );
376 desired <<= 1;
377 }
378 if( desired > (1 << 16))
379 desired = (1 << 16);
380
381 count = *(highTable++);
382
383 // find curves bracketing the desired value
384 do {
385 highAccl = Fetch32( highTable );
386 highTable += 2;
387 highPoints = *(highTable++);
388
389 if( desired <= highAccl)
390 break;
391
392 lowTable = highTable;
393 lowAccl = highAccl;
394 lowPoints = highPoints;
395 highTable += lowPoints * 4;
396
397 } while( true );
398
399 // scale between the two
400 if( lowTable)
401 scale = IOFixedDivide( desired - lowAccl,
402 highAccl - lowAccl );
403 // or take all the high one
404 else {
405 scale = (1 << 16);
406 lowTable = highTable;
407 lowAccl = highAccl;
408 lowPoints = 0;
409 }
410
411 if( lowPoints > highPoints)
412 segCount = lowPoints;
413 else
414 segCount = highPoints;
415 segCount *= 2;
416/* IOLog("lowPoints %ld, highPoints %ld, segCount %ld\n",
417 lowPoints, highPoints, segCount); */
418 segments = IONew( CursorDeviceSegment, segCount );
419 assert( segments );
420 segment = segments;
421
422 x1 = prevX1 = y1 = prevY1 = 0;
423
424 lowerX = Fetch32( lowTable );
425 lowTable += 2;
426 lowerY = Fetch32( lowTable );
427 lowTable += 2;
428 upperX = Fetch32( highTable );
429 highTable += 2;
430 upperY = Fetch32( highTable );
431 highTable += 2;
432
433 do {
434 // consume next point from first X
435 lower = (lowPoints && (!highPoints || (lowerX <= upperX)));
436
437 if( lower) {
438 /* highline */
439 x2 = upperX;
440 y2 = upperY;
441 x3 = lowerX;
442 y3 = lowerY;
443 if( lowPoints && (--lowPoints)) {
444 lowerX = Fetch32( lowTable );
445 lowTable += 2;
446 lowerY = Fetch32( lowTable );
447 lowTable += 2;
448 }
449 } else {
450 /* lowline */
451 x2 = lowerX;
452 y2 = lowerY;
453 x3 = upperX;
454 y3 = upperY;
455 if( highPoints && (--highPoints)) {
456 upperX = Fetch32( highTable );
457 highTable += 2;
458 upperY = Fetch32( highTable );
459 highTable += 2;
460 }
461 }
462 {
463 // convert to line segment
464 assert( segment < (segments + segCount) );
465
466 scaledX2 = IOFixedMultiply( devScale, /* newX */ x3 );
467 scaledY2 = IOFixedMultiply( crsrScale,
468 /* newY */ Interpolate( x1, y1, x2, y2, x3, y3,
469 scale, lower ) );
470 if( lowPoints || highPoints)
471 segment->devUnits = scaledX2;
472 else
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); */
480
481 scaledX1 = scaledX2;
482 scaledY1 = scaledY2;
483 segment++;
484 }
485
486 // continue on from last point
487 if( lowPoints && highPoints) {
488 if( lowerX > upperX) {
489 prevX1 = x1;
490 prevY1 = y1;
491 } else {
492 /* swaplines */
493 prevX1 = x1;
494 prevY1 = y1;
495 x1 = x3;
496 y1 = y3;
497 }
498 } else {
499 x2 = x1;
500 y2 = y1;
501 x1 = prevX1;
502 y1 = prevY1;
503 prevX1 = x2;
504 prevY1 = y2;
505 }
506
507 } while( lowPoints || highPoints );
508
509 if( _scaleSegCount && _scaleSegments)
510 IODelete( _scaleSegments,
511 CursorDeviceSegment, _scaleSegCount );
512 _scaleSegCount = segCount;
513 _scaleSegments = (void *) segments;
514
515 _fractX = _fractY = 0;
516
517 data->release();
518}
519
520
521bool IOHIPointing::resetPointer()
522{
523 IOTakeLock( _deviceLock);
524
525 _buttonMode = NX_RightButton;
526// IOLog("***resetPointer -- glob_accel = %08lx", glob_accel );
527 if( glob_accel > 0 )
528 {
529 // Restore the last acceleration value, since we may have been hot
530 // unplugged and re-plugged.
531 setupForAcceleration(glob_accel);
532 }
533 else
534 {
535 setupForAcceleration(0x8000);
536 }
537 updateProperties();
538
539 IOUnlock( _deviceLock);
540 return true;
541}
542
543void IOHIPointing::dispatchAbsolutePointerEvent(Point * newLoc,
544 Bounds * bounds,
545 UInt32 buttonState,
546 bool proximity,
547 int pressure,
548 int pressureMin,
549 int pressureMax,
550 int stylusAngle,
551 AbsoluteTime ts)
552{
553 int buttons = 0;
554 int dx, dy;
555
556 IOTakeLock(_deviceLock);
557
558 if (buttonState & 1) {
559 buttons |= EV_LB;
560 }
561
562 if (buttonCount() > 1) {
563 if (buttonState & -2) { // any other buttons
564 buttons |= EV_RB;
565 }
566 }
567
568 if ((_pressureThresholdToClick < 255) && ((pressure - pressureMin) > ((pressureMax - pressureMin) * _pressureThresholdToClick / 256))) {
569 buttons |= EV_LB;
570 }
571
572 if (_buttonMode == NX_OneButton) {
573 if ((buttons & (EV_LB|EV_RB)) != 0) {
574 buttons = EV_LB;
575 }
576 }
577
578 if (_convertAbsoluteToRelative) {
579 dx = newLoc->x - _previousLocation.x;
580 dy = newLoc->y - _previousLocation.y;
581
582 if ((_contactToMove && !_hadContact && (pressure > pressureMin)) || (abs(dx) > ((bounds->maxx - bounds->minx) / 20)) || (abs(dy) > ((bounds->maxy - bounds->miny) / 20))) {
583 dx = 0;
584 dy = 0;
585 } else {
586 scalePointer(&dx, &dy);
587 }
588
589 _previousLocation.x = newLoc->x;
590 _previousLocation.y = newLoc->y;
591 }
592
593 IOUnlock(_deviceLock);
594
595 _hadContact = (pressure > pressureMin);
596
597 if (!_contactToMove || (pressure > pressureMin)) {
598 pressure -= pressureMin;
599 if (pressure > 255) {
600 pressure = 255;
601 }
602 if (_convertAbsoluteToRelative) {
603 if (_relativePointerEventAction && _relativePointerEventTarget) {
604 (*_relativePointerEventAction)(_relativePointerEventTarget,
605 buttons,
606 dx,
607 dy,
608 ts);
609 }
610 } else {
611 if (_absolutePointerEventAction && _absolutePointerEventTarget) {
612 (*_absolutePointerEventAction)(_absolutePointerEventTarget,
613 buttons,
614 newLoc,
615 bounds,
616 proximity,
617 pressure,
618 stylusAngle,
619 ts);
620 }
621 }
622 }
623
624 return;
625}
626
627void IOHIPointing::dispatchRelativePointerEvent(int dx,
628 int dy,
629 UInt32 buttonState,
630 AbsoluteTime ts)
631{
632 int buttons;
633
634 IOTakeLock( _deviceLock);
635
636 buttons = 0;
637
638 if( buttonState & 1)
639 buttons |= EV_LB;
640
641 if( buttonCount() > 1) {
642 if( buttonState & 2) // any others down
643 buttons |= EV_RB;
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
646 if(buttonState & 4)
647 buttons |= 2;
648 // Add in the rest of the buttons in a linear fasion...
649 buttons |= buttonState & ~0x7;
650 }
651
652 // Perform pointer acceleration computations
653 scalePointer(&dx, &dy);
654
655 // Perform button tying and mapping. This
656 // stuff applies to relative posn devices (mice) only.
657 if ( _buttonMode == NX_OneButton )
658 {
659 // Remap both Left and Right (but no others?) to Left.
660 if ( (buttons & (EV_LB|EV_RB)) != 0 ) {
661 buttons |= EV_LB;
662 buttons &= ~EV_RB;
663 }
664 }
665 else if ( (buttonCount() > 1) && (_buttonMode == NX_LeftButton) )
666 // Menus on left button. Swap!
667 {
668 int temp = 0;
669 if ( buttons & EV_LB )
670 temp = EV_RB;
671 if ( buttons & EV_RB )
672 temp |= EV_LB;
673 // Swap Left and Right, preserve everything else
674 buttons = (buttons & ~(EV_LB|EV_RB)) | temp;
675 }
676 IOUnlock( _deviceLock);
677
678 if (_relativePointerEventAction) /* upstream call */
679 {
680 (*_relativePointerEventAction)(_relativePointerEventTarget,
681 /* buttons */ buttons,
682 /* deltaX */ dx,
683 /* deltaY */ dy,
684 /* atTime */ ts);
685 }
686}
687
688void IOHIPointing::dispatchScrollWheelEvent(short deltaAxis1,
689 short deltaAxis2,
690 short deltaAxis3,
691 AbsoluteTime ts)
692{
693 if (_scrollWheelEventAction) {
694 (*_scrollWheelEventAction)(_scrollWheelEventTarget,
695 deltaAxis1,
696 deltaAxis2,
697 deltaAxis3,
698 ts);
699 }
700}
701
702bool IOHIPointing::updateProperties( void )
703{
704 bool ok;
705 UInt32 res = resolution();
706
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));
714
715 return( ok & super::updateProperties() );
716}
717
718IOReturn IOHIPointing::setParamProperties( OSDictionary * dict )
719{
720 OSData * data;
721 IOReturn err = kIOReturnSuccess;
722 bool updated = false;
723 UInt8 * bytes;
724
725 IOTakeLock( _deviceLock);
726 if( (data = OSDynamicCast( OSData,
727 dict->getObject(kIOHIDPointerAccelerationKey)))) {
728
729 setupForAcceleration( *((IOFixed *)data->getBytesNoCopy()) );
730 updated = true;
731 }
732 IOUnlock( _deviceLock);
733
734 if( dict->getObject(kIOHIDResetPointerKey))
735 resetPointer();
736
737 if ((data = OSDynamicCast(OSData,
738 dict->getObject(kIOHIDPointerConvertAbsoluteKey)))) {
739 bytes = (UInt8 *) data->getBytesNoCopy();
740 _convertAbsoluteToRelative = (bytes[0] != 0) ? true : false;
741 updated = true;
742 }
743
744 if ((data = OSDynamicCast(OSData,
745 dict->getObject(kIOHIDPointerContactToMoveKey)))) {
746 bytes = (UInt8 *) data->getBytesNoCopy();
747 _contactToMove = (bytes[0] != 0) ? true : false;
748 updated = true;
749 }
750
751 if( updated )
752 updateProperties();
753
754 return( err );
755}
756
757// subclasses override
758
759IOItemCount IOHIPointing::buttonCount()
760{
761 return (1);
762}
763
764IOFixed IOHIPointing::resolution()
765{
766 return (100 << 16);
767}
768
769OSData * IOHIPointing::copyAccelerationTable()
770{
771static const UInt16 accl[] = {
772 0x0000, 0x8000,
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,
779 0x0000
780};
781
782 OSData * data = OSDynamicCast( OSData,
783 getProperty( "HIDPointerAccelerationTable" ));
784 if( data)
785 data->retain();
786 else
787 data = OSData::withBytesNoCopy( accl, sizeof( accl ) );
788
789 return( data );
790}