]> git.saurik.com Git - apple/xnu.git/blob - iokit/Drivers/platform/drvApplePS2Controller/ApplePS2Controller.cpp
xnu-123.5.tar.gz
[apple/xnu.git] / iokit / Drivers / platform / drvApplePS2Controller / ApplePS2Controller.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/IOService.h>
25 #include <IOKit/IOSyncer.h>
26 #include <IOKit/IOCommandQueue.h>
27 #include <IOKit/ps2/ApplePS2KeyboardDevice.h>
28 #include <IOKit/ps2/ApplePS2MouseDevice.h>
29 #include "ApplePS2Controller.h"
30
31 extern "C"
32 {
33 #include <architecture/i386/pio.h>
34 #include <machine/machine_routines.h>
35 }
36
37 static ApplePS2Controller * gApplePS2Controller = 0; // global variable to self
38
39 // =============================================================================
40 // Interrupt-Time Support Functions
41 //
42
43 static void interruptHandlerMouse(OSObject *, void *, IOService *, int)
44 {
45 //
46 // Wake our workloop to service the interrupt. This is an edge-triggered
47 // interrupt, so returning from this routine without clearing the interrupt
48 // condition is perfectly normal.
49 //
50
51 gApplePS2Controller->_interruptSourceMouse->interruptOccurred(0, 0, 0);
52 }
53
54 // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
55
56 static void interruptHandlerKeyboard(OSObject *, void *, IOService *, int)
57 {
58 #if DEBUGGER_SUPPORT
59 //
60 // The keyboard interrupt handler reads in the pending scan code and stores
61 // it on our internal queue; should it completes a debugger escape sequence,
62 // we jump to the debugger function immediately.
63 //
64
65 UInt8 key;
66 UInt8 status;
67
68 // Lock out the keyboard interrupt handler [redundant here] and claim
69 // exclusive access to the internal keyboard queue.
70
71 gApplePS2Controller->lockController();
72
73 // Verify that data is available on the controller's input port.
74
75 if ( ((status = inb(kCommandPort)) & kOutputReady) )
76 {
77 // Verify that the data is keyboard data, otherwise call mouse handler.
78 // This case should never really happen, but if it does, we handle it.
79
80 if ( (status & kMouseData) )
81 {
82 interruptHandlerMouse(0, 0, 0, 0);
83 }
84 else
85 {
86 // Retrieve the keyboard data on the controller's input port.
87
88 key = inb(kDataPort);
89
90 // Call the debugger-key-sequence checking code (if a debugger sequence
91 // completes, the debugger function will be invoked immediately within
92 // doEscape). The doEscape call may insist that we drop the scan code
93 // we just received in some cases (a true return) -- we don't question
94 // it's judgement and comply.
95
96 if (gApplePS2Controller->doEscape(key) == false)
97 gApplePS2Controller->enqueueKeyboardData(key);
98
99 // In all cases, we wake up our workloop to service the interrupt data.
100 gApplePS2Controller->_interruptSourceKeyboard->interruptOccurred(0, 0, 0);
101 }
102 }
103
104 // Remove the lockout on the keyboard interrupt handler [ineffective here]
105 // and release our exclusive access to the internal keyboard queue.
106
107 gApplePS2Controller->unlockController();
108 #else
109 //
110 // Wake our workloop to service the interrupt. This is an edge-triggered
111 // interrupt, so returning from this routine without clearing the interrupt
112 // condition is perfectly normal.
113 //
114
115 gApplePS2Controller->_interruptSourceKeyboard->interruptOccurred(0, 0, 0);
116
117 #endif DEBUGGER_SUPPORT
118 }
119
120 // =============================================================================
121 // ApplePS2Controller Class Implementation
122 //
123
124 #define super IOService
125 OSDefineMetaClassAndStructors(ApplePS2Controller, IOService);
126
127 bool ApplePS2Controller::init(OSDictionary * properties)
128 {
129 if (!super::init(properties)) return false;
130
131 //
132 // Initialize minimal state.
133 //
134
135 _commandQueue = 0;
136 _workLoop = 0;
137
138 _interruptSourceKeyboard = 0;
139 _interruptSourceMouse = 0;
140
141 _interruptTargetKeyboard = 0;
142 _interruptTargetMouse = 0;
143
144 _interruptActionKeyboard = NULL;
145 _interruptActionMouse = NULL;
146
147 _interruptInstalledKeyboard = false;
148 _interruptInstalledMouse = false;
149
150 _mouseDevice = 0;
151 _keyboardDevice = 0;
152
153 #if DEBUGGER_SUPPORT
154 _extendedState = false;
155 _modifierState = 0x00;
156
157 _keyboardQueueAlloc = NULL;
158 queue_init(&_keyboardQueue);
159 queue_init(&_keyboardQueueUnused);
160
161 _controllerLockOldSpl = 0;
162 usimple_lock_init(&_controllerLock, ETAP_NO_TRACE);
163 #endif DEBUGGER_SUPPORT
164
165 return true;
166 }
167
168 // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
169
170 bool ApplePS2Controller::start(IOService * provider)
171 {
172 //
173 // The driver has been instructed to start. Allocate all our resources.
174 //
175
176 if (!super::start(provider)) return false;
177
178 #if DEBUGGER_SUPPORT
179 _keyboardQueueAlloc = (KeyboardQueueElement *)
180 IOMalloc(kKeyboardQueueSize*sizeof(KeyboardQueueElement));
181 if (!_keyboardQueueAlloc) return false;
182
183 // Add the allocated keyboard queue entries to "unused" queue.
184 for (int index = 0; index < kKeyboardQueueSize; index++)
185 queue_enter(&_keyboardQueueUnused, &_keyboardQueueAlloc[index],
186 KeyboardQueueElement *, chain);
187 #endif DEBUGGER_SUPPORT
188
189 //
190 // Initialize the mouse and keyboard hardware to a known state -- the IRQs
191 // are disabled (don't want interrupts), the clock line is enabled (want to
192 // be able to send commands), and the device itself is disabled (don't want
193 // asynchronous data arrival for key/mouse events). We call the read/write
194 // port routines directly, since no other thread will conflict with us.
195 //
196
197 UInt8 commandByte;
198 writeCommandPort(kCP_GetCommandByte);
199 commandByte = readDataPort(kDT_Keyboard);
200 commandByte &= ~(kCB_EnableMouseIRQ | kCB_DisableMouseClock);
201 writeCommandPort(kCP_SetCommandByte);
202 writeDataPort(commandByte);
203
204 writeDataPort(kDP_SetDefaultsAndDisable);
205 readDataPort(kDT_Keyboard); // (discard acknowledge; success irrelevant)
206
207 writeCommandPort(kCP_TransmitToMouse);
208 writeDataPort(kDP_SetDefaultsAndDisable);
209 readDataPort(kDT_Mouse); // (discard acknowledge; success irrelevant)
210
211 //
212 // Clear out garbage in the controller's input streams, before starting up
213 // the work loop.
214 //
215
216 while ( inb(kCommandPort) & kOutputReady )
217 {
218 inb(kDataPort);
219 IODelay(kDataDelay);
220 }
221
222 //
223 // Initialize our work loop, our command queue, and our interrupt event
224 // sources. The work loop can accept requests after this step.
225 //
226
227 _workLoop = IOWorkLoop::workLoop();
228 _commandQueue = IOCommandQueue::commandQueue(
229 this, (IOCommandQueueAction) &ApplePS2Controller::processRequest);
230 _interruptSourceMouse = IOInterruptEventSource::interruptEventSource(
231 this, (IOInterruptEventAction) &ApplePS2Controller::interruptOccurred);
232 _interruptSourceKeyboard = IOInterruptEventSource::interruptEventSource(
233 this, (IOInterruptEventAction) &ApplePS2Controller::interruptOccurred);
234
235 if ( !_workLoop ||
236 !_commandQueue ||
237 !_interruptSourceMouse ||
238 !_interruptSourceKeyboard ) return false;
239
240 if ( _workLoop->addEventSource(_commandQueue) != kIOReturnSuccess )
241 return false;
242
243 //
244 // Create the keyboard nub and the mouse nub. The keyboard and mouse drivers
245 // will query these nubs to determine the existence of the keyboard or mouse,
246 // and should they exist, will attach themselves to the nub as clients.
247 //
248
249 _keyboardDevice = new ApplePS2KeyboardDevice;
250
251 if ( !_keyboardDevice ||
252 !_keyboardDevice->init() ||
253 !_keyboardDevice->attach(this) ) return false;
254
255 _mouseDevice = new ApplePS2MouseDevice;
256
257 if ( !_mouseDevice ||
258 !_mouseDevice->init() ||
259 !_mouseDevice->attach(this) ) return false;
260
261 gApplePS2Controller = this;
262
263 _keyboardDevice->registerService();
264 _mouseDevice->registerService();
265
266 return true; // success
267 }
268
269 // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
270
271 void ApplePS2Controller::stop(IOService * provider)
272 {
273 //
274 // The driver has been instructed to stop. Note that we must break all
275 // connections to other service objects now (ie. no registered actions,
276 // no pointers and retains to objects, etc), if any.
277 //
278
279 // Ensure that the interrupt handlers have been uninstalled (ie. no clients).
280 assert(_interruptInstalledKeyboard == false);
281 assert(_interruptInstalledMouse == false);
282
283 // Free the nubs we created.
284 if (_keyboardDevice) _keyboardDevice->release();
285 if (_mouseDevice) _mouseDevice->release();
286
287 // Free the work loop.
288 if (_workLoop) _workLoop->release();
289
290 // Free the interrupt source and command queue.
291 if (_commandQueue) _commandQueue->release();
292 if (_interruptSourceKeyboard) _interruptSourceKeyboard->release();
293 if (_interruptSourceMouse) _interruptSourceMouse->release();
294
295 #if DEBUGGER_SUPPORT
296 // Free the keyboard queue allocation space (after disabling interrupt).
297 if (_keyboardQueueAlloc)
298 IOFree(_keyboardQueueAlloc,kKeyboardQueueSize*sizeof(KeyboardQueueElement));
299 #endif DEBUGGER_SUPPORT
300
301 gApplePS2Controller = 0;
302
303 super::stop(provider);
304 }
305
306 // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
307
308 IOWorkLoop * ApplePS2Controller::getWorkLoop() const
309 {
310 return _workLoop;
311 }
312
313 // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
314
315 void ApplePS2Controller::installInterruptAction(PS2DeviceType deviceType,
316 OSObject * target,
317 PS2InterruptAction action)
318 {
319 //
320 // Install the keyboard or mouse interrupt handler.
321 //
322 // This method assumes only one possible mouse and only one possible
323 // keyboard client (ie. callers), and assumes two distinct interrupt
324 // handlers for each, hence needs no protection against races.
325 //
326
327 // Is it the keyboard or the mouse interrupt handler that was requested?
328 // We only install it if it is currently uninstalled.
329
330 if (deviceType == kDT_Keyboard && _interruptInstalledKeyboard == false)
331 {
332 target->retain();
333 _interruptTargetKeyboard = target;
334 _interruptActionKeyboard = action;
335 _workLoop->addEventSource(_interruptSourceKeyboard);
336 getProvider()->registerInterrupt(kIRQ_Keyboard,0, interruptHandlerKeyboard);
337 getProvider()->enableInterrupt(kIRQ_Keyboard);
338 _interruptInstalledKeyboard = true;
339 }
340
341 else if (deviceType == kDT_Mouse && _interruptInstalledMouse == false)
342 {
343 target->retain();
344 _interruptTargetMouse = target;
345 _interruptActionMouse = action;
346 _workLoop->addEventSource(_interruptSourceMouse);
347 getProvider()->registerInterrupt(kIRQ_Mouse, 0, interruptHandlerMouse);
348 getProvider()->enableInterrupt(kIRQ_Mouse);
349 _interruptInstalledMouse = true;
350 }
351 }
352
353 // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
354
355 void ApplePS2Controller::uninstallInterruptAction(PS2DeviceType deviceType)
356 {
357 //
358 // Uninstall the keyboard or mouse interrupt handler.
359 //
360 // This method assumes only one possible mouse and only one possible
361 // keyboard client (ie. callers), and assumes two distinct interrupt
362 // handlers for each, hence needs no protection against races.
363 //
364
365 // Is it the keyboard or the mouse interrupt handler that was requested?
366 // We only install it if it is currently uninstalled.
367
368 if (deviceType == kDT_Keyboard && _interruptInstalledKeyboard == true)
369 {
370 getProvider()->disableInterrupt(kIRQ_Keyboard);
371 getProvider()->unregisterInterrupt(kIRQ_Keyboard);
372 _workLoop->removeEventSource(_interruptSourceMouse);
373 _interruptInstalledKeyboard = false;
374 _interruptActionKeyboard = NULL;
375 _interruptTargetKeyboard->release();
376 _interruptTargetKeyboard = 0;
377 }
378
379 else if (deviceType == kDT_Mouse && _interruptInstalledMouse == true)
380 {
381 getProvider()->disableInterrupt(kIRQ_Mouse);
382 getProvider()->unregisterInterrupt(kIRQ_Mouse);
383 _workLoop->removeEventSource(_interruptSourceMouse);
384 _interruptInstalledMouse = false;
385 _interruptActionMouse = NULL;
386 _interruptTargetMouse->release();
387 _interruptTargetMouse = 0;
388 }
389 }
390
391 // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
392
393 PS2Request * ApplePS2Controller::allocateRequest()
394 {
395 //
396 // Allocate a request structure. Blocks until successful. Request structure
397 // is guaranteed to be zeroed.
398 //
399
400 PS2Request * request = (PS2Request *) IOMalloc(sizeof(PS2Request));
401 bzero(request, sizeof(PS2Request));
402 return request;
403 }
404
405 // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
406
407 void ApplePS2Controller::freeRequest(PS2Request * request)
408 {
409 //
410 // Deallocate a request structure.
411 //
412
413 IOFree(request, sizeof(PS2Request));
414 }
415
416 // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
417
418 bool ApplePS2Controller::submitRequest(PS2Request * request)
419 {
420 //
421 // Submit the request to the controller for processing, asynchronously.
422 //
423
424 return (_commandQueue->enqueueCommand(false, request) == KERN_SUCCESS);
425 }
426
427 // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
428
429 void ApplePS2Controller::submitRequestAndBlock(PS2Request * request)
430 {
431 //
432 // Submit the request to the controller for processing, synchronously.
433 //
434
435 IOSyncer * completionSyncer = IOSyncer::create();
436
437 assert(completionSyncer);
438 request->completionTarget = this;
439 request->completionAction = submitRequestAndBlockCompletion;
440 request->completionParam = completionSyncer;
441
442 _commandQueue->enqueueCommand(true, request);
443
444 completionSyncer->wait(); // wait 'till done
445 }
446
447 // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
448
449 void ApplePS2Controller::submitRequestAndBlockCompletion(void *, void * param)
450 { // PS2CompletionAction
451 IOSyncer * completionSyncer = (IOSyncer *) param;
452 completionSyncer->signal();
453 }
454
455 // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
456
457 void ApplePS2Controller::interruptOccurred(IOInterruptEventSource *, int)
458 { // IOInterruptEventAction
459 //
460 // Our work loop has informed us of an interrupt, that is, asynchronous
461 // data has arrived on our input stream. Read the data and dispatch it
462 // to the appropriate driver.
463 //
464 // This method should only be called from our single-threaded work loop.
465 //
466
467 UInt8 status;
468
469 #if DEBUGGER_SUPPORT
470 lockController(); // (lock out interrupt + access to queue)
471 while (1)
472 {
473 // See if data is available on the keyboard input stream (off queue);
474 // we do not read keyboard data from the real data port if it should
475 // be available.
476
477 if (dequeueKeyboardData(&status))
478 {
479 unlockController();
480 dispatchDriverInterrupt(kDT_Keyboard, status);
481 lockController();
482 }
483
484 // See if data is available on the mouse input stream (off real port).
485
486 else if ( (inb(kCommandPort) & (kOutputReady | kMouseData)) ==
487 (kOutputReady | kMouseData))
488 {
489 unlockController();
490 dispatchDriverInterrupt(kDT_Mouse, inb(kDataPort));
491 lockController();
492 }
493 else break; // out of loop
494 }
495 unlockController(); // (release interrupt lockout + access to queue)
496 #else
497 // Loop only while there is data currently on the input stream.
498
499 while ( ((status = inb(kCommandPort)) & kOutputReady) )
500 {
501 // Read in and dispatch the data, but only if it isn't what is required
502 // by the active command.
503
504 dispatchDriverInterrupt((status&kMouseData)?kDT_Mouse:kDT_Keyboard,
505 inb(kDataPort));
506 }
507 #endif DEBUGGER_SUPPORT
508 }
509
510 // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
511
512 void ApplePS2Controller::dispatchDriverInterrupt(PS2DeviceType deviceType,
513 UInt8 data)
514 {
515 //
516 // The supplied data is passed onto the interrupt handler in the appropriate
517 // driver, if one is registered, otherwise the data byte is thrown away.
518 //
519 // This method should only be called from our single-threaded work loop.
520 //
521
522 if ( deviceType == kDT_Mouse )
523 {
524 // Dispatch the data to the mouse driver.
525 if (_interruptInstalledMouse)
526 (*_interruptActionMouse)(_interruptTargetMouse, data);
527 }
528 else if ( deviceType == kDT_Keyboard )
529 {
530 // Dispatch the data to the keyboard driver.
531 if (_interruptInstalledKeyboard)
532 (*_interruptActionKeyboard)(_interruptTargetKeyboard, data);
533 }
534 }
535
536 // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
537
538 void ApplePS2Controller::processRequest(PS2Request * request,
539 void * /* field1 */,
540 void * /* field2 */,
541 void * /* field3 */)
542 // IOCommandQueueAction
543 {
544 //
545 // Our work loop has informed us of a request submission. Process
546 // the request. Note that this code "figures out" when the mouse
547 // input stream should be read over the keyboard input stream.
548 //
549 // This method should only be called from our single-threaded work loop.
550 //
551
552 UInt8 byte;
553 PS2DeviceType deviceMode = kDT_Keyboard;
554 bool failed = false;
555 bool transmitToMouse = false;
556 unsigned index;
557
558 // Process each of the commands in the list.
559
560 for (index = 0; index < request->commandsCount; index++)
561 {
562 switch (request->commands[index].command)
563 {
564 case kPS2C_ReadDataPort:
565 request->commands[index].inOrOut = readDataPort(deviceMode);
566 break;
567
568 case kPS2C_ReadDataPortAndCompare:
569 #if OUT_OF_ORDER_DATA_CORRECTION_FEATURE
570 byte = readDataPort(deviceMode, request->commands[index].inOrOut);
571 #else
572 byte = readDataPort(deviceMode);
573 #endif
574 failed = (byte != request->commands[index].inOrOut);
575 break;
576
577 case kPS2C_WriteDataPort:
578 writeDataPort(request->commands[index].inOrOut);
579 if (transmitToMouse) // next reads from mouse input stream
580 {
581 deviceMode = kDT_Mouse;
582 transmitToMouse = false;
583 }
584 else
585 {
586 deviceMode = kDT_Keyboard;
587 }
588 break;
589
590 case kPS2C_WriteCommandPort:
591 writeCommandPort(request->commands[index].inOrOut);
592 if (request->commands[index].inOrOut == kCP_TransmitToMouse)
593 transmitToMouse = true; // preparing to transmit data to mouse
594 break;
595 }
596
597 if (failed) break;
598 }
599
600 // If a command failed and stopped the request processing, store its
601 // index into the commandsCount field.
602
603 if (failed) request->commandsCount = index;
604
605 // Invoke the completion routine, if one was supplied.
606
607 if (request->completionTarget && request->completionAction)
608 {
609 (*request->completionAction)(request->completionTarget,
610 request->completionParam);
611 }
612 }
613
614 // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
615
616 UInt8 ApplePS2Controller::readDataPort(PS2DeviceType deviceType)
617 {
618 //
619 // Blocks until keyboard or mouse data is available from the controller
620 // and returns that data. Note, if mouse data is requested but keyboard
621 // data is what is available, the data is delivered to the appropriate
622 // driver interrupt routine immediately (effectively, the request is
623 // "preempted" temporarily).
624 //
625 // There is a built-in timeout for this command of (timeoutCounter X
626 // kDataDelay) microseconds, approximately.
627 //
628 // This method should only be called from our single-threaded work loop.
629 //
630
631 UInt8 readByte;
632 UInt8 status;
633 UInt32 timeoutCounter = 10000; // (timeoutCounter * kDataDelay = 70 ms)
634
635 while (1)
636 {
637 #if DEBUGGER_SUPPORT
638 lockController(); // (lock out interrupt + access to queue)
639 if (deviceType == kDT_Keyboard && dequeueKeyboardData(&readByte))
640 {
641 unlockController();
642 return readByte;
643 }
644 #endif DEBUGGER_SUPPORT
645
646 //
647 // Wait for the controller's output buffer to become ready.
648 //
649
650 while (timeoutCounter && !((status = inb(kCommandPort)) & kOutputReady))
651 {
652 timeoutCounter--;
653 IODelay(kDataDelay);
654 }
655
656 //
657 // If we timed out, something went awfully wrong; return a fake value.
658 //
659
660 if (timeoutCounter == 0)
661 {
662 #if DEBUGGER_SUPPORT
663 unlockController(); // (release interrupt lockout + access to queue)
664 #endif DEBUGGER_SUPPORT
665
666 IOLog("%s: Timed out on %s input stream.\n", getName(),
667 (deviceType == kDT_Keyboard) ? "keyboard" : "mouse");
668 return 0;
669 }
670
671 //
672 // Read in the data. We return the data, however, only if it arrived on
673 // the requested input stream.
674 //
675
676 readByte = inb(kDataPort);
677
678 #if DEBUGGER_SUPPORT
679 unlockController(); // (release interrupt lockout + access to queue)
680 #endif DEBUGGER_SUPPORT
681
682 if ( (status & kMouseData) )
683 {
684 if (deviceType == kDT_Mouse) return readByte;
685 }
686 else
687 {
688 if (deviceType == kDT_Keyboard) return readByte;
689 }
690
691 //
692 // The data we just received is for the other input stream, not the one
693 // that was requested, so dispatch other device's interrupt handler.
694 //
695
696 dispatchDriverInterrupt((deviceType==kDT_Keyboard)?kDT_Mouse:kDT_Keyboard,
697 readByte);
698 } // while (forever)
699 }
700
701 // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
702
703 #if OUT_OF_ORDER_DATA_CORRECTION_FEATURE
704
705 UInt8 ApplePS2Controller::readDataPort(PS2DeviceType deviceType,
706 UInt8 expectedByte)
707 {
708 //
709 // Blocks until keyboard or mouse data is available from the controller
710 // and returns that data. Note, if mouse data is requested but keyboard
711 // data is what is available, the data is delivered to the appropriate
712 // driver interrupt routine immediately (effectively, the request is
713 // "preempted" temporarily).
714 //
715 // There is a built-in timeout for this command of (timeoutCounter X
716 // kDataDelay) microseconds, approximately.
717 //
718 // This method should only be called from our single-threaded work loop.
719 //
720 // This version of readDataPort does exactly the same as the original,
721 // except that if the value that should be read from the (appropriate)
722 // input stream is not what is expected, we make these assumptions:
723 //
724 // (a) the data byte we did get was "asynchronous" data being sent by
725 // the device, which has not figured out that it has to respond to
726 // the command we just sent to it.
727 // (b) that the real "expected" response will be the next byte in the
728 // stream; so what we do is put aside the first byte we read and
729 // wait for the next byte; if it's the expected value, we dispatch
730 // the first byte we read to the driver's interrupt handler, then
731 // return the expected byte. The caller will have never known that
732 // asynchronous data arrived at a very bad time.
733 // (c) that the real "expected" response will arrive within (kDataDelay
734 // X timeoutCounter) microseconds from the time the call is made.
735 //
736
737 UInt8 firstByte = 0;
738 bool firstByteHeld = false;
739 UInt8 readByte;
740 bool requestedStream;
741 UInt8 status;
742 UInt32 timeoutCounter = 10000; // (timeoutCounter * kDataDelay = 70 ms)
743
744 while (1)
745 {
746 #if DEBUGGER_SUPPORT
747 lockController(); // (lock out interrupt + access to queue)
748 if (deviceType == kDT_Keyboard && dequeueKeyboardData(&readByte))
749 {
750 requestedStream = true;
751 goto skipForwardToY;
752 }
753 #endif DEBUGGER_SUPPORT
754
755 //
756 // Wait for the controller's output buffer to become ready.
757 //
758
759 while (timeoutCounter && !((status = inb(kCommandPort)) & kOutputReady))
760 {
761 timeoutCounter--;
762 IODelay(kDataDelay);
763 }
764
765 //
766 // If we timed out, we return the first byte we read, unless THIS IS the
767 // first byte we are trying to read, then something went awfully wrong
768 // and we return a fake value rather than lock up the controller longer.
769 //
770
771 if (timeoutCounter == 0)
772 {
773 #if DEBUGGER_SUPPORT
774 unlockController(); // release interrupt lockout + access to queue
775 #endif DEBUGGER_SUPPORT
776
777 if (firstByteHeld) return firstByte;
778
779 IOLog("%s: Timed out on %s input stream.\n", getName(),
780 (deviceType == kDT_Keyboard) ? "keyboard" : "mouse");
781 return 0;
782 }
783
784 //
785 // Read in the data. We process the data, however, only if it arrived on
786 // the requested input stream.
787 //
788
789 readByte = inb(kDataPort);
790 requestedStream = false;
791
792 if ( (status & kMouseData) )
793 {
794 if (deviceType == kDT_Mouse) requestedStream = true;
795 }
796 else
797 {
798 if (deviceType == kDT_Keyboard) requestedStream = true;
799 }
800
801 #if DEBUGGER_SUPPORT
802 skipForwardToY:
803 unlockController(); // (release interrupt lockout + access to queue)
804 #endif DEBUGGER_SUPPORT
805
806 if (requestedStream)
807 {
808 if (readByte == expectedByte)
809 {
810 if (firstByteHeld == false)
811 {
812 //
813 // Normal case. Return first byte received.
814 //
815
816 return readByte;
817 }
818 else
819 {
820 //
821 // Our assumption was correct. The second byte matched. Dispatch
822 // the first byte to the interrupt handler, and return the second.
823 //
824
825 dispatchDriverInterrupt(deviceType, firstByte);
826 return readByte;
827 }
828 }
829 else // (readByte does not match expectedByte)
830 {
831 if (firstByteHeld == false)
832 {
833 //
834 // The first byte was received, and does not match the byte we are
835 // expecting. Put it aside for the moment.
836 //
837
838 firstByteHeld = true;
839 firstByte = readByte;
840 }
841 else if (readByte != expectedByte)
842 {
843 //
844 // The second byte mismatched as well. I have yet to see this case
845 // occur [Dan], however I do think it's plausible. No error logged.
846 //
847
848 dispatchDriverInterrupt(deviceType, readByte);
849 return firstByte;
850 }
851 }
852 }
853 else
854 {
855 //
856 // The data we just received is for the other input stream, not ours,
857 // so dispatch appropriate interrupt handler.
858 //
859
860 dispatchDriverInterrupt((deviceType==kDT_Keyboard)?kDT_Mouse:kDT_Keyboard,
861 readByte);
862 }
863 } // while (forever)
864 }
865
866 #endif
867
868 // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
869
870 void ApplePS2Controller::writeDataPort(UInt8 byte)
871 {
872 //
873 // Block until room in the controller's input buffer is available, then
874 // write the given byte to the Data Port.
875 //
876 // This method should only be dispatched from our single-threaded work loop.
877 //
878
879 while (inb(kCommandPort) & kInputBusy) IODelay(kDataDelay);
880 outb(kDataPort, byte);
881 }
882
883 // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
884
885 void ApplePS2Controller::writeCommandPort(UInt8 byte)
886 {
887 //
888 // Block until room in the controller's input buffer is available, then
889 // write the given byte to the Command Port.
890 //
891 // This method should only be dispatched from our single-threaded work loop.
892 //
893
894 while (inb(kCommandPort) & kInputBusy) IODelay(kDataDelay);
895 outb(kCommandPort, byte);
896 }
897
898 // =============================================================================
899 // Escape-Key Processing Stuff Localized Here (eg. Mini-Monitor)
900 //
901
902 #if DEBUGGER_SUPPORT
903
904 #define kModifierShiftLeft 0x01
905 #define kModifierShiftRight 0x02
906 #define kModifierCtrlLeft 0x04
907 #define kModifierCtrlRight 0x08
908 #define kModifierAltLeft 0x10
909 #define kModifierAltRight 0x20
910 #define kModifierWindowsLeft 0x40
911 #define kModifierWindowsRight 0x80
912
913 #define kModifierShiftMask (kModifierShiftLeft | kModifierShiftRight )
914 #define kModifierCtrlMask (kModifierCtrlLeft | kModifierCtrlRight )
915 #define kModifierAltMask (kModifierAltLeft | kModifierAltRight )
916 #define kModifierWindowsMask (kModifierWindowsLeft | kModifierWindowsRight)
917
918 bool ApplePS2Controller::doEscape(UInt8 scancode)
919 {
920 static struct
921 {
922 UInt8 scancode;
923 UInt8 extended;
924 UInt16 modifier;
925 } modifierTable[] = { { kSC_Alt, false, kModifierAltLeft },
926 { kSC_Alt, true, kModifierAltRight },
927 { kSC_Ctrl, false, kModifierCtrlLeft },
928 { kSC_Ctrl, true, kModifierCtrlRight },
929 { kSC_ShiftLeft, false, kModifierShiftLeft },
930 { kSC_ShiftRight, false, kModifierShiftRight },
931 { kSC_WindowsLeft, true, kModifierWindowsLeft },
932 { kSC_WindowsRight, true, kModifierWindowsRight },
933 { 0, 0, 0 } };
934
935 UInt32 index;
936 bool releaseModifiers = false;
937 bool upBit = (scancode & kSC_UpBit) ? true : false;
938
939 //
940 // See if this is an extened scancode sequence.
941 //
942
943 if (scancode == kSC_Extend)
944 {
945 _extendedState = true;
946 return false;
947 }
948
949 //
950 // Update the modifier state, if applicable.
951 //
952
953 scancode &= ~kSC_UpBit;
954
955 for (index = 0; modifierTable[index].scancode; index++)
956 {
957 if ( modifierTable[index].scancode == scancode &&
958 modifierTable[index].extended == _extendedState )
959 {
960 if (upBit) _modifierState &= ~modifierTable[index].modifier;
961 else _modifierState |= modifierTable[index].modifier;
962
963 _extendedState = false;
964 return false;
965 }
966 }
967
968 //
969 // Call the debugger function, if applicable.
970 //
971
972 if (scancode == kSC_Delete) // (both extended and non-extended scancodes)
973 {
974 if ( _modifierState == kModifierAltLeft ||
975 _modifierState == kModifierAltRight )
976 {
977 // Disable the mouse by forcing the clock line low.
978
979 while (inb(kCommandPort) & kInputBusy) IODelay(kDataDelay);
980 outb(kCommandPort, kCP_DisableMouseClock);
981
982 // Call the debugger function.
983
984 Debugger("Programmer Key");
985
986 // Re-enable the mouse by making the clock line active.
987
988 while (inb(kCommandPort) & kInputBusy) IODelay(kDataDelay);
989 outb(kCommandPort, kCP_EnableMouseClock);
990
991 releaseModifiers = true;
992 }
993 }
994
995 //
996 // Release all the modifier keys that were down before the debugger
997 // function was called (assumption is that they are no longer held
998 // down after the debugger function returns).
999 //
1000
1001 if (releaseModifiers)
1002 {
1003 for (index = 0; modifierTable[index].scancode; index++)
1004 {
1005 if ( _modifierState & modifierTable[index].modifier )
1006 {
1007 if (modifierTable[index].extended) enqueueKeyboardData(kSC_Extend);
1008 enqueueKeyboardData(modifierTable[index].scancode | kSC_UpBit);
1009 }
1010 }
1011 _modifierState = 0x00;
1012 }
1013
1014 //
1015 // Update all other state and return status.
1016 //
1017
1018 _extendedState = false;
1019 return (releaseModifiers);
1020 }
1021
1022 void ApplePS2Controller::enqueueKeyboardData(UInt8 key)
1023 {
1024 //
1025 // Enqueue the supplied keyboard data onto our internal queues. The
1026 // controller must already be locked.
1027 //
1028
1029 KeyboardQueueElement * element;
1030
1031 // Obtain an unused keyboard data element.
1032 if (!queue_empty(&_keyboardQueueUnused))
1033 {
1034 queue_remove_first(&_keyboardQueueUnused,
1035 element, KeyboardQueueElement *, chain);
1036
1037 // Store the new keyboard data element on the queue.
1038 element->data = key;
1039 queue_enter(&_keyboardQueue, element, KeyboardQueueElement *, chain);
1040 }
1041 }
1042
1043 bool ApplePS2Controller::dequeueKeyboardData(UInt8 * key)
1044 {
1045 //
1046 // Dequeue keyboard data from our internal queues, if the queue is not
1047 // empty. Should the queue be empty, false is returned. The controller
1048 // must already be locked.
1049 //
1050
1051 KeyboardQueueElement * element;
1052
1053 // Obtain an unused keyboard data element.
1054 if (!queue_empty(&_keyboardQueue))
1055 {
1056 queue_remove_first(&_keyboardQueue, element, KeyboardQueueElement *, chain);
1057 *key = element->data;
1058
1059 // Place the unused keyboard data element onto the unused queue.
1060 queue_enter(&_keyboardQueueUnused, element, KeyboardQueueElement *, chain);
1061
1062 return true;
1063 }
1064 return false;
1065 }
1066
1067 void ApplePS2Controller::unlockController(void)
1068 {
1069 usimple_unlock(&_controllerLock);
1070 ml_set_interrupts_enabled(_controllerLockOldSpl);
1071 }
1072
1073 void ApplePS2Controller::lockController(void)
1074 {
1075 int oldSpl = ml_set_interrupts_enabled(FALSE);
1076 usimple_lock(&_controllerLock);
1077 _controllerLockOldSpl = oldSpl;
1078 }
1079
1080 #endif DEBUGGER_SUPPORT