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 #include <IOKit/assert.h>
24 #include <IOKit/IOLib.h>
25 #include "ApplePS2Mouse.h"
27 // =============================================================================
28 // ApplePS2Mouse Class Implementation
31 #define super IOHIPointing
32 OSDefineMetaClassAndStructors(ApplePS2Mouse
, IOHIPointing
);
34 UInt32
ApplePS2Mouse::deviceType() { return NX_EVS_DEVICE_TYPE_MOUSE
; };
35 UInt32
ApplePS2Mouse::interfaceID() { return NX_EVS_DEVICE_INTERFACE_BUS_ACE
; };
37 IOItemCount
ApplePS2Mouse::buttonCount() { return 3; };
38 IOFixed
ApplePS2Mouse::resolution() { return _resolution
; };
40 // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
42 bool ApplePS2Mouse::init(OSDictionary
* properties
)
45 // Initialize this object's minimal state. This is invoked right after this
46 // object is instantiated.
49 if (!super::init(properties
)) return false;
52 _interruptHandlerInstalled
= false;
54 _packetLength
= kPacketLengthStandard
;
55 _resolution
= (150) << 16; // (default is 150 dpi; 6 counts/mm)
56 _type
= kMouseTypeStandard
;
61 // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
63 ApplePS2Mouse
* ApplePS2Mouse::probe(IOService
* provider
, SInt32
* score
)
66 // The driver has been instructed to verify the presence of the actual
67 // hardware we represent. We are guaranteed by the controller that the
68 // mouse clock is enabled and the mouse itself is disabled (thus it
69 // won't send any asynchronous mouse data that may mess up the
70 // responses expected by the commands we send it).
73 ApplePS2MouseDevice
* device
= (ApplePS2MouseDevice
*)provider
;
74 PS2Request
* request
= device
->allocateRequest();
77 if (!super::probe(provider
, score
)) return 0;
80 // Check to see if acknowledges are being received for commands to the mouse.
83 // (get information command)
84 request
->commands
[0].command
= kPS2C_WriteCommandPort
;
85 request
->commands
[0].inOrOut
= kCP_TransmitToMouse
;
86 request
->commands
[1].command
= kPS2C_WriteDataPort
;
87 request
->commands
[1].inOrOut
= kDP_GetMouseInformation
;
88 request
->commands
[2].command
= kPS2C_ReadDataPortAndCompare
;
89 request
->commands
[2].inOrOut
= kSC_Acknowledge
;
90 request
->commands
[3].command
= kPS2C_ReadDataPort
;
91 request
->commands
[3].inOrOut
= 0;
92 request
->commands
[4].command
= kPS2C_ReadDataPort
;
93 request
->commands
[4].inOrOut
= 0;
94 request
->commands
[5].command
= kPS2C_ReadDataPort
;
95 request
->commands
[5].inOrOut
= 0;
96 request
->commandsCount
= 6;
97 device
->submitRequestAndBlock(request
);
100 success
= (request
->commandsCount
== 6);
101 device
->freeRequest(request
);
103 return (success
) ? this : 0;
106 // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
108 bool ApplePS2Mouse::start(IOService
* provider
)
111 // The driver has been instructed to start. This is called after a
112 // successful probe and match.
115 if (!super::start(provider
)) return false;
118 // Maintain a pointer to and retain the provider object.
121 _device
= (ApplePS2MouseDevice
*)provider
;
125 // Install our driver's interrupt handler, for asynchronous data delivery.
128 _device
->installInterruptAction(this,
129 (PS2InterruptAction
)&ApplePS2Mouse::interruptOccurred
);
130 _interruptHandlerInstalled
= true;
133 // Obtain our mouse's resolution and sampling rate.
136 switch (getMouseInformation() & 0x00FF00)
138 case 0x0000: _resolution
= (25) << 16; break; // 25 dpi
139 case 0x0100: _resolution
= (50) << 16; break; // 50 dpi
140 case 0x0200: _resolution
= (100) << 16; break; // 100 dpi
141 case 0x0300: _resolution
= (200) << 16; break; // 200 dpi
142 default: _resolution
= (150) << 16; break; // 150 dpi
146 // Enable the Intellimouse mode, should this be an Intellimouse.
149 if ( setIntellimouseMode() == true )
151 _packetLength
= kPacketLengthIntellimouse
;
152 _type
= kMouseTypeIntellimouse
;
156 // Enable the mouse clock (should already be so) and the mouse IRQ line.
159 setCommandByte(kCB_EnableMouseIRQ
, kCB_DisableMouseClock
);
162 // Finally, we enable the mouse itself, so that it may start reporting
166 setMouseEnable(true);
171 // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
173 void ApplePS2Mouse::stop(IOService
* provider
)
176 // The driver has been instructed to stop. Note that we must break all
177 // connections to other service objects now (ie. no registered actions,
178 // no pointers and retains to objects, etc), if any.
181 assert(_device
== provider
);
184 // Disable the mouse itself, so that it may stop reporting mouse events.
187 setMouseEnable(false);
190 // Disable the mouse clock and the mouse IRQ line.
193 setCommandByte(kCB_DisableMouseClock
, kCB_EnableMouseIRQ
);
196 // Uninstall the interrupt handler.
199 if ( _interruptHandlerInstalled
) _device
->uninstallInterruptAction();
200 _interruptHandlerInstalled
= false;
203 // Release the pointer to the provider object.
209 super::stop(provider
);
212 // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
214 void ApplePS2Mouse::interruptOccurred(UInt8 data
) // PS2InterruptAction
217 // This will be invoked automatically from our device when asynchronous mouse
218 // needs to be delivered. Process the mouse data. Do NOT send any BLOCKING
219 // commands to our device in this context.
221 // We ignore all bytes until we see the start of a packet, otherwise the mouse
222 // packets may get out of sequence and things will get very confusing.
225 if (_packetByteCount
== 0 && ((data
== kSC_Acknowledge
) || !(data
& 0x08)))
227 IOLog("%s: Unexpected data from PS/2 controller.\n", getName());
232 // Add this byte to the packet buffer. If the packet is complete, that is,
233 // we have the three bytes, dispatch this packet for processing.
236 _packetBuffer
[_packetByteCount
++] = data
;
238 if (_packetByteCount
== _packetLength
)
240 dispatchRelativePointerEventWithPacket(_packetBuffer
);
241 _packetByteCount
= 0;
245 // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
247 void ApplePS2Mouse::dispatchRelativePointerEventWithPacket(UInt8
* packet
)
250 // Process the three byte mouse packet that was retreived from the mouse.
251 // The format of the bytes is as follows:
254 // YO XO YS XS 1 M R L
255 // X7 X6 X5 X4 X3 X3 X1 X0
256 // Y7 Y6 Y5 Y4 Y3 Y2 Y1 Y0
257 // Z7 Z6 Z5 Z4 Z3 Z2 Z1 Z0 <- fourth byte returned only for Intellimouse type
266 if ( !(packet
[0] & 0x1) ) buttons
|= 0x1; // left button (bit 0 in packet)
267 if ( !(packet
[0] & 0x2) ) buttons
|= 0x2; // right button (bit 1 in packet)
268 if ( !(packet
[0] & 0x4) ) buttons
|= 0x4; // middle button (bit 2 in packet)
270 dx
= ((packet
[0] & 0x10) ? 0xffffff00 : 0 ) | packet
[1];
271 dy
= -(((packet
[0] & 0x20) ? 0xffffff00 : 0 ) | packet
[2]);
272 dz
= (SInt32
)((SInt8
)packet
[3]);
274 clock_get_uptime(&now
);
276 dispatchRelativePointerEvent(dx
, dy
, buttons
, now
);
281 // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
283 void ApplePS2Mouse::setMouseEnable(bool enable
)
286 // Instructs the mouse to start or stop the reporting of mouse events.
287 // Be aware that while the mouse is enabled, asynchronous mouse events
288 // may arrive in the middle of command sequences sent to the controller,
289 // and may get confused for expected command responses.
291 // It is safe to issue this request from the interrupt/completion context.
294 PS2Request
* request
= _device
->allocateRequest();
296 // (mouse enable/disable command)
297 request
->commands
[0].command
= kPS2C_WriteCommandPort
;
298 request
->commands
[0].inOrOut
= kCP_TransmitToMouse
;
299 request
->commands
[1].command
= kPS2C_WriteDataPort
;
300 request
->commands
[1].inOrOut
= (enable
)?kDP_Enable
:kDP_SetDefaultsAndDisable
;
301 request
->commands
[2].command
= kPS2C_ReadDataPortAndCompare
;
302 request
->commands
[2].inOrOut
= kSC_Acknowledge
;
303 request
->commandsCount
= 3;
304 _device
->submitRequest(request
); // asynchronous, auto-free'd
307 // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
309 void ApplePS2Mouse::setMouseSampleRate(UInt8 sampleRate
)
312 // Instructs the mouse to change its sampling rate to the given value, in
313 // reports per second.
315 // It is safe to issue this request from the interrupt/completion context.
318 PS2Request
* request
= _device
->allocateRequest();
320 // (set mouse sample rate command)
321 request
->commands
[0].command
= kPS2C_WriteCommandPort
;
322 request
->commands
[0].inOrOut
= kCP_TransmitToMouse
;
323 request
->commands
[1].command
= kPS2C_WriteDataPort
;
324 request
->commands
[1].inOrOut
= kDP_SetMouseSampleRate
;
325 request
->commands
[2].command
= kPS2C_ReadDataPortAndCompare
;
326 request
->commands
[2].inOrOut
= kSC_Acknowledge
;
327 request
->commands
[3].command
= kPS2C_WriteCommandPort
;
328 request
->commands
[3].inOrOut
= kCP_TransmitToMouse
;
329 request
->commands
[4].command
= kPS2C_WriteDataPort
;
330 request
->commands
[4].inOrOut
= sampleRate
;
331 request
->commands
[5].command
= kPS2C_ReadDataPortAndCompare
;
332 request
->commands
[5].inOrOut
= kSC_Acknowledge
;
333 request
->commandsCount
= 6;
334 _device
->submitRequest(request
); // asynchronous, auto-free'd
337 // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
339 bool ApplePS2Mouse::setIntellimouseMode()
342 // Determines whether this mouse is a Microsoft Intellimouse, and if it is,
343 // it enables it (the mouse will send 4 byte packets for mouse events from
344 // then on). Returns true if the Intellimouse mode was succesfully enabled.
346 // Do NOT issue this request from the interrupt/completion context.
353 // Obtain the current sample rate, in order that we may restore it after
354 // the Intellimouse command sequence completes.
357 mouseInfo
= getMouseInformation();
359 if (mouseInfo
== (UInt32
)(-1)) return false;
362 // Generate the special command sequence to enable the 'Intellimouse' mode.
363 // The sequence is to set the sampling rate to 200, 100, then 80, at which
364 // point the mouse will start sending 4 byte packets for mouse events and
365 // return a mouse ID of 3.
368 setMouseSampleRate(200);
369 setMouseSampleRate(100);
370 setMouseSampleRate(80 );
373 // Determine whether we have an Intellimouse by asking for the mouse's ID.
376 isIntellimouse
= ( getMouseID() == kMouseTypeIntellimouse
);
379 // Restore the original sampling rate, before we obliterated it.
382 setMouseSampleRate(mouseInfo
& 0x0000FF);
384 return isIntellimouse
;
387 // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
389 UInt32
ApplePS2Mouse::getMouseInformation()
392 // Asks the mouse to transmit its three information bytes. Should the
393 // mouse not respond, a value of (UInt32)(-1) is returned.
395 // Do NOT issue this request from the interrupt/completion context.
398 PS2Request
* request
= _device
->allocateRequest();
399 UInt32 returnValue
= (UInt32
)(-1);
401 // (get information command)
402 request
->commands
[0].command
= kPS2C_WriteCommandPort
;
403 request
->commands
[0].inOrOut
= kCP_TransmitToMouse
;
404 request
->commands
[1].command
= kPS2C_WriteDataPort
;
405 request
->commands
[1].inOrOut
= kDP_GetMouseInformation
;
406 request
->commands
[2].command
= kPS2C_ReadDataPortAndCompare
;
407 request
->commands
[2].inOrOut
= kSC_Acknowledge
;
408 request
->commands
[3].command
= kPS2C_ReadDataPort
;
409 request
->commands
[3].inOrOut
= 0;
410 request
->commands
[4].command
= kPS2C_ReadDataPort
;
411 request
->commands
[4].inOrOut
= 0;
412 request
->commands
[5].command
= kPS2C_ReadDataPort
;
413 request
->commands
[5].inOrOut
= 0;
414 request
->commandsCount
= 6;
415 _device
->submitRequestAndBlock(request
);
417 if (request
->commandsCount
== 6) // success?
419 returnValue
= ((UInt32
)request
->commands
[3].inOrOut
<< 16) |
420 ((UInt32
)request
->commands
[4].inOrOut
<< 8 ) |
421 ((UInt32
)request
->commands
[5].inOrOut
);
423 _device
->freeRequest(request
);
428 // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
430 UInt8
ApplePS2Mouse::getMouseID()
433 // Asks the mouse to transmit its identification byte. Should the mouse
434 // not respond, a value of (UInt8)(-1) is returned.
436 // Note that some documentation on PS/2 mice implies that two identification
437 // bytes are returned and not one. This was proven to be false in my tests.
439 // Do NOT issue this request from the interrupt/completion context.
442 PS2Request
* request
= _device
->allocateRequest();
443 UInt8 returnValue
= (UInt8
)(-1);
445 // (get information command)
446 request
->commands
[0].command
= kPS2C_WriteCommandPort
;
447 request
->commands
[0].inOrOut
= kCP_TransmitToMouse
;
448 request
->commands
[1].command
= kPS2C_WriteDataPort
;
449 request
->commands
[1].inOrOut
= kDP_GetId
;
450 request
->commands
[2].command
= kPS2C_ReadDataPortAndCompare
;
451 request
->commands
[2].inOrOut
= kSC_Acknowledge
;
452 request
->commands
[3].command
= kPS2C_ReadDataPort
;
453 request
->commands
[3].inOrOut
= 0;
454 request
->commandsCount
= 4;
455 _device
->submitRequestAndBlock(request
);
457 if (request
->commandsCount
== 4) // success?
458 returnValue
= request
->commands
[3].inOrOut
;
460 _device
->freeRequest(request
);
465 // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
467 void ApplePS2Mouse::setCommandByte(UInt8 setBits
, UInt8 clearBits
)
470 // Sets the bits setBits and clears the bits clearBits "atomically" in the
471 // controller's Command Byte. Since the controller does not provide such
472 // a read-modify-write primitive, we resort to a test-and-set try loop.
474 // Do NOT issue this request from the interrupt/completion context.
478 UInt8 commandByteNew
;
479 PS2Request
* request
= _device
->allocateRequest();
483 // (read command byte)
484 request
->commands
[0].command
= kPS2C_WriteCommandPort
;
485 request
->commands
[0].inOrOut
= kCP_GetCommandByte
;
486 request
->commands
[1].command
= kPS2C_ReadDataPort
;
487 request
->commands
[1].inOrOut
= 0;
488 request
->commandsCount
= 2;
489 _device
->submitRequestAndBlock(request
);
492 // Modify the command byte as requested by caller.
495 commandByte
= request
->commands
[1].inOrOut
;
496 commandByteNew
= (commandByte
| setBits
) & (~clearBits
);
498 // ("test-and-set" command byte)
499 request
->commands
[0].command
= kPS2C_WriteCommandPort
;
500 request
->commands
[0].inOrOut
= kCP_GetCommandByte
;
501 request
->commands
[1].command
= kPS2C_ReadDataPortAndCompare
;
502 request
->commands
[1].inOrOut
= commandByte
;
503 request
->commands
[2].command
= kPS2C_WriteCommandPort
;
504 request
->commands
[2].inOrOut
= kCP_SetCommandByte
;
505 request
->commands
[3].command
= kPS2C_WriteDataPort
;
506 request
->commands
[3].inOrOut
= commandByteNew
;
507 request
->commandsCount
= 4;
508 _device
->submitRequestAndBlock(request
);
511 // Repeat this loop if last command failed, that is, if the old command byte
512 // was modified since we first read it.
515 } while (request
->commandsCount
!= 4);
517 _device
->freeRequest(request
);