]> git.saurik.com Git - apple/xnu.git/blob - iokit/Drivers/hidsystem/drvApplePS2Mouse/ApplePS2Mouse.cpp
xnu-124.13.tar.gz
[apple/xnu.git] / iokit / Drivers / hidsystem / drvApplePS2Mouse / ApplePS2Mouse.cpp
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 #include <IOKit/assert.h>
24 #include <IOKit/IOLib.h>
25 #include "ApplePS2Mouse.h"
26
27 // =============================================================================
28 // ApplePS2Mouse Class Implementation
29 //
30
31 #define super IOHIPointing
32 OSDefineMetaClassAndStructors(ApplePS2Mouse, IOHIPointing);
33
34 UInt32 ApplePS2Mouse::deviceType() { return NX_EVS_DEVICE_TYPE_MOUSE; };
35 UInt32 ApplePS2Mouse::interfaceID() { return NX_EVS_DEVICE_INTERFACE_BUS_ACE; };
36
37 IOItemCount ApplePS2Mouse::buttonCount() { return 3; };
38 IOFixed ApplePS2Mouse::resolution() { return _resolution; };
39
40 // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
41
42 bool ApplePS2Mouse::init(OSDictionary * properties)
43 {
44 //
45 // Initialize this object's minimal state. This is invoked right after this
46 // object is instantiated.
47 //
48
49 if (!super::init(properties)) return false;
50
51 _device = 0;
52 _interruptHandlerInstalled = false;
53 _packetByteCount = 0;
54 _packetLength = kPacketLengthStandard;
55 _resolution = (150) << 16; // (default is 150 dpi; 6 counts/mm)
56 _type = kMouseTypeStandard;
57
58 return true;
59 }
60
61 // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
62
63 ApplePS2Mouse * ApplePS2Mouse::probe(IOService * provider, SInt32 * score)
64 {
65 //
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).
71 //
72
73 ApplePS2MouseDevice * device = (ApplePS2MouseDevice *)provider;
74 PS2Request * request = device->allocateRequest();
75 bool success;
76
77 if (!super::probe(provider, score)) return 0;
78
79 //
80 // Check to see if acknowledges are being received for commands to the mouse.
81 //
82
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);
98
99 // (free the request)
100 success = (request->commandsCount == 6);
101 device->freeRequest(request);
102
103 return (success) ? this : 0;
104 }
105
106 // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
107
108 bool ApplePS2Mouse::start(IOService * provider)
109 {
110 //
111 // The driver has been instructed to start. This is called after a
112 // successful probe and match.
113 //
114
115 if (!super::start(provider)) return false;
116
117 //
118 // Maintain a pointer to and retain the provider object.
119 //
120
121 _device = (ApplePS2MouseDevice *)provider;
122 _device->retain();
123
124 //
125 // Install our driver's interrupt handler, for asynchronous data delivery.
126 //
127
128 _device->installInterruptAction(this,
129 (PS2InterruptAction)&ApplePS2Mouse::interruptOccurred);
130 _interruptHandlerInstalled = true;
131
132 //
133 // Obtain our mouse's resolution and sampling rate.
134 //
135
136 switch (getMouseInformation() & 0x00FF00)
137 {
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
143 }
144
145 //
146 // Enable the Intellimouse mode, should this be an Intellimouse.
147 //
148
149 if ( setIntellimouseMode() == true )
150 {
151 _packetLength = kPacketLengthIntellimouse;
152 _type = kMouseTypeIntellimouse;
153 }
154
155 //
156 // Enable the mouse clock (should already be so) and the mouse IRQ line.
157 //
158
159 setCommandByte(kCB_EnableMouseIRQ, kCB_DisableMouseClock);
160
161 //
162 // Finally, we enable the mouse itself, so that it may start reporting
163 // mouse events.
164 //
165
166 setMouseEnable(true);
167
168 return true;
169 }
170
171 // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
172
173 void ApplePS2Mouse::stop(IOService * provider)
174 {
175 //
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.
179 //
180
181 assert(_device == provider);
182
183 //
184 // Disable the mouse itself, so that it may stop reporting mouse events.
185 //
186
187 setMouseEnable(false);
188
189 //
190 // Disable the mouse clock and the mouse IRQ line.
191 //
192
193 setCommandByte(kCB_DisableMouseClock, kCB_EnableMouseIRQ);
194
195 //
196 // Uninstall the interrupt handler.
197 //
198
199 if ( _interruptHandlerInstalled ) _device->uninstallInterruptAction();
200 _interruptHandlerInstalled = false;
201
202 //
203 // Release the pointer to the provider object.
204 //
205
206 _device->release();
207 _device = 0;
208
209 super::stop(provider);
210 }
211
212 // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
213
214 void ApplePS2Mouse::interruptOccurred(UInt8 data) // PS2InterruptAction
215 {
216 //
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.
220 //
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.
223 //
224
225 if (_packetByteCount == 0 && ((data == kSC_Acknowledge) || !(data & 0x08)))
226 {
227 IOLog("%s: Unexpected data from PS/2 controller.\n", getName());
228 return;
229 }
230
231 //
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.
234 //
235
236 _packetBuffer[_packetByteCount++] = data;
237
238 if (_packetByteCount == _packetLength)
239 {
240 dispatchRelativePointerEventWithPacket(_packetBuffer);
241 _packetByteCount = 0;
242 }
243 }
244
245 // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
246
247 void ApplePS2Mouse::dispatchRelativePointerEventWithPacket(UInt8 * packet)
248 {
249 //
250 // Process the three byte mouse packet that was retreived from the mouse.
251 // The format of the bytes is as follows:
252 //
253 // 7 6 5 4 3 2 1 0
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
258 //
259
260 UInt32 buttons = 0;
261 SInt32 dx;
262 SInt32 dy;
263 SInt32 dz;
264 AbsoluteTime now;
265
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)
269
270 dx = ((packet[0] & 0x10) ? 0xffffff00 : 0 ) | packet[1];
271 dy = -(((packet[0] & 0x20) ? 0xffffff00 : 0 ) | packet[2]);
272 dz = (SInt32)((SInt8)packet[3]);
273
274 clock_get_uptime(&now);
275
276 dispatchRelativePointerEvent(dx, dy, buttons, now);
277
278 return;
279 }
280
281 // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
282
283 void ApplePS2Mouse::setMouseEnable(bool enable)
284 {
285 //
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.
290 //
291 // It is safe to issue this request from the interrupt/completion context.
292 //
293
294 PS2Request * request = _device->allocateRequest();
295
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
305 }
306
307 // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
308
309 void ApplePS2Mouse::setMouseSampleRate(UInt8 sampleRate)
310 {
311 //
312 // Instructs the mouse to change its sampling rate to the given value, in
313 // reports per second.
314 //
315 // It is safe to issue this request from the interrupt/completion context.
316 //
317
318 PS2Request * request = _device->allocateRequest();
319
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
335 }
336
337 // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
338
339 bool ApplePS2Mouse::setIntellimouseMode()
340 {
341 //
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.
345 //
346 // Do NOT issue this request from the interrupt/completion context.
347 //
348
349 UInt32 mouseInfo;
350 bool isIntellimouse;
351
352 //
353 // Obtain the current sample rate, in order that we may restore it after
354 // the Intellimouse command sequence completes.
355 //
356
357 mouseInfo = getMouseInformation();
358
359 if (mouseInfo == (UInt32)(-1)) return false;
360
361 //
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.
366 //
367
368 setMouseSampleRate(200);
369 setMouseSampleRate(100);
370 setMouseSampleRate(80 );
371
372 //
373 // Determine whether we have an Intellimouse by asking for the mouse's ID.
374 //
375
376 isIntellimouse = ( getMouseID() == kMouseTypeIntellimouse );
377
378 //
379 // Restore the original sampling rate, before we obliterated it.
380 //
381
382 setMouseSampleRate(mouseInfo & 0x0000FF);
383
384 return isIntellimouse;
385 }
386
387 // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
388
389 UInt32 ApplePS2Mouse::getMouseInformation()
390 {
391 //
392 // Asks the mouse to transmit its three information bytes. Should the
393 // mouse not respond, a value of (UInt32)(-1) is returned.
394 //
395 // Do NOT issue this request from the interrupt/completion context.
396 //
397
398 PS2Request * request = _device->allocateRequest();
399 UInt32 returnValue = (UInt32)(-1);
400
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);
416
417 if (request->commandsCount == 6) // success?
418 {
419 returnValue = ((UInt32)request->commands[3].inOrOut << 16) |
420 ((UInt32)request->commands[4].inOrOut << 8 ) |
421 ((UInt32)request->commands[5].inOrOut);
422 }
423 _device->freeRequest(request);
424
425 return returnValue;
426 }
427
428 // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
429
430 UInt8 ApplePS2Mouse::getMouseID()
431 {
432 //
433 // Asks the mouse to transmit its identification byte. Should the mouse
434 // not respond, a value of (UInt8)(-1) is returned.
435 //
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.
438 //
439 // Do NOT issue this request from the interrupt/completion context.
440 //
441
442 PS2Request * request = _device->allocateRequest();
443 UInt8 returnValue = (UInt8)(-1);
444
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);
456
457 if (request->commandsCount == 4) // success?
458 returnValue = request->commands[3].inOrOut;
459
460 _device->freeRequest(request);
461
462 return returnValue;
463 }
464
465 // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
466
467 void ApplePS2Mouse::setCommandByte(UInt8 setBits, UInt8 clearBits)
468 {
469 //
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.
473 //
474 // Do NOT issue this request from the interrupt/completion context.
475 //
476
477 UInt8 commandByte;
478 UInt8 commandByteNew;
479 PS2Request * request = _device->allocateRequest();
480
481 do
482 {
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);
490
491 //
492 // Modify the command byte as requested by caller.
493 //
494
495 commandByte = request->commands[1].inOrOut;
496 commandByteNew = (commandByte | setBits) & (~clearBits);
497
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);
509
510 //
511 // Repeat this loop if last command failed, that is, if the old command byte
512 // was modified since we first read it.
513 //
514
515 } while (request->commandsCount != 4);
516
517 _device->freeRequest(request);
518 }