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 #ifndef _APPLEPS2CONTROLLER_H
24 #define _APPLEPS2CONTROLLER_H
26 #include <IOKit/IOInterruptEventSource.h>
27 #include <IOKit/IOService.h>
28 #include <IOKit/IOWorkLoop.h>
29 #include <IOKit/IOCommandQueue.h>
30 #include <IOKit/ps2/ApplePS2Device.h>
32 class ApplePS2KeyboardDevice
;
33 class ApplePS2MouseDevice
;
36 // This section describes the problem with the PS/2 controller design and what
37 // we are doing about it (OUT_OF_ORDER_DATA_CORRECTION_FEATURE).
39 // While the controller processes requests sent by the client drivers, at some
40 // point in most requests, a read needs to be made from the data port to check
41 // an acknowledge or receive some sort of data. We illustrate this issue with
42 // an example -- a write LEDs request to the keyboard:
44 // 1. Write Write LED command.
45 // 2. Read 0xFA Verify the acknowledge (0xFA).
46 // 3. Write Write LED state.
47 // 4. Read 0xFA Verify the acknowledge (0xFA).
49 // The problem is that the keyboard (when it is enabled) can send key events
50 // to the controller at any time, including when the controller is expecting
51 // to read an acknowledge next. What ends up happening is this sequence:
53 // a. Write Write LED command.
54 // b. Read 0x21 Keyboard reports [F] key was depressed, not realizing that
55 // we're still expecting a response to the command we JUST
56 // sent the keyboard. We receive 0x21 as a response to our
57 // command, and figure the command failed.
58 // c. Get 0xFA Keyboard NOW decides to respond to the command with an
59 // acknowledge. We're not waiting to read anything, so
60 // this byte gets dispatched to the driver's interrupt
61 // handler, which spews out an error message saying it
62 // wasn't expecting an acknowledge.
64 // What can we do about this? In the above case, we can take note of the fact
65 // that we are specifically looking for the 0xFA acknowledgement byte (through
66 // the information passed in the kPS2C_ReadAndCompare primitive). If we don't
67 // receive this byte next on the input data stream, we put the byte we did get
68 // aside for a moment, and give the keyboard (or mouse) a second chance to
71 // If we receive the 0xFA acknowledgement byte on the second read, that we
72 // assume that situation described above just happened. We transparently
73 // dispatch the first byte to the driver's interrupt handler, where it was
74 // meant to go, and return the second correct byte to the read-and-compare
75 // logic, where it was meant to go. Everyone wins.
77 // The only situation this feature cannot help is where a kPS2C_ReadDataPort
78 // primitive is issued in place of a kPS2C_ReadDataPortAndCompare primitive.
79 // This is necessary in some requests because the driver does not know what
80 // it is going to receive. This can be illustrated in the mouse get info
83 // 1. Write Prepare to write to mouse.
84 // 2. Write Write information command.
85 // 3. Read 0xFA Verify the acknowledge (0xFA). __-> mouse can report mouse
86 // 4. Read Get first information byte. __-> packet bytes in between
87 // 5. Read Get second information byte. __-> these reads
88 // 6. Rrad Get third information byte.
90 // Controller cannot build any defenses against this. It is suggested that the
91 // driver writer disable the mouse first, then send any dangerous commands, and
92 // re-enable the mouse when the command completes.
94 // Note that the OUT_OF_ORDER_DATA_CORRECTION_FEATURE can be turned off at
95 // compile time. Please see the readDataPort:expecting: method for more
96 // information about the assumptions necessary for this feature.
99 // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
103 // Enable debugger support (eg. mini-monitor).
105 #define DEBUGGER_SUPPORT 1
107 // Enable dynamic "second chance" re-ordering of input stream data if a
108 // command response fails to match the expected byte.
110 #define OUT_OF_ORDER_DATA_CORRECTION_FEATURE 1
112 // PS/2 device types.
114 typedef enum { kDT_Keyboard
, kDT_Mouse
} PS2DeviceType
;
116 // Interrupt definitions.
118 #define kIRQ_Keyboard 1
119 #define kIRQ_Mouse 12
120 #define kIPL_Keyboard 6
125 #define kDataDelay 7 // usec to delay before data is valid
127 // Ports used to control the PS/2 keyboard/mouse and read data from it.
129 #define kDataPort 0x60 // keyboard data & cmds (read/write)
130 #define kCommandPort 0x64 // keybd status (read), command (write)
132 // Bit definitions for kCommandPort read values (status).
134 #define kOutputReady 0x01 // output (from keybd) buffer full
135 #define kInputBusy 0x02 // input (to keybd) buffer full
136 #define kSystemFlag 0x04 // "System Flag"
137 #define kCommandLastSent 0x08 // 1 = cmd, 0 = data last sent
138 #define kKeyboardInhibited 0x10 // 0 if keyboard inhibited
139 #define kMouseData 0x20 // mouse data available
142 // Definitions for our internal keyboard queue (holds keys processed by the
143 // interrupt-time mini-monitor-key-sequence detection code).
145 #define kKeyboardQueueSize 32 // number of KeyboardQueueElements
147 typedef struct KeyboardQueueElement KeyboardQueueElement
;
148 struct KeyboardQueueElement
153 #endif DEBUGGER_SUPPORT
155 // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
156 // ApplePS2Controller Class Declaration
159 class ApplePS2Controller
: public IOService
161 OSDeclareDefaultStructors(ApplePS2Controller
);
163 public: // interrupt-time variables and functions
164 IOInterruptEventSource
* _interruptSourceKeyboard
;
165 IOInterruptEventSource
* _interruptSourceMouse
;
168 void lockController(void);
169 void unlockController(void);
171 bool doEscape(UInt8 key
);
172 bool dequeueKeyboardData(UInt8
* key
);
173 void enqueueKeyboardData(UInt8 key
);
174 #endif DEBUGGER_SUPPORT
177 IOCommandQueue
* _commandQueue
;
178 IOWorkLoop
* _workLoop
;
180 OSObject
* _interruptTargetKeyboard
;
181 OSObject
* _interruptTargetMouse
;
182 PS2InterruptAction _interruptActionKeyboard
;
183 PS2InterruptAction _interruptActionMouse
;
184 bool _interruptInstalledKeyboard
;
185 bool _interruptInstalledMouse
;
187 ApplePS2MouseDevice
* _mouseDevice
; // mouse nub
188 ApplePS2KeyboardDevice
* _keyboardDevice
; // keyboard nub
191 usimple_lock_data_t _controllerLock
; // mach simple spin lock
192 int _controllerLockOldSpl
; // spl before lock taken
194 KeyboardQueueElement
* _keyboardQueueAlloc
; // queues' allocation space
195 queue_head_t _keyboardQueue
; // queue of available keys
196 queue_head_t _keyboardQueueUnused
; // queue of unused entries
199 UInt16 _modifierState
;
200 #endif DEBUGGER_SUPPORT
202 virtual void dispatchDriverInterrupt(PS2DeviceType deviceType
, UInt8 data
);
203 virtual void interruptOccurred(IOInterruptEventSource
*, int);
204 virtual void processRequest(PS2Request
* request
, void *, void *, void *);
205 static void submitRequestAndBlockCompletion(void *, void * param
);
207 virtual UInt8
readDataPort(PS2DeviceType deviceType
);
208 virtual void writeCommandPort(UInt8 byte
);
209 virtual void writeDataPort(UInt8 byte
);
211 #if OUT_OF_ORDER_DATA_CORRECTION_FEATURE
212 virtual UInt8
readDataPort(PS2DeviceType deviceType
, UInt8 expectedByte
);
216 virtual bool init(OSDictionary
* properties
);
217 virtual bool start(IOService
* provider
);
218 virtual void stop(IOService
* provider
);
220 virtual IOWorkLoop
* getWorkLoop() const;
222 virtual void installInterruptAction(PS2DeviceType deviceType
,
224 PS2InterruptAction action
);
225 virtual void uninstallInterruptAction(PS2DeviceType deviceType
);
227 virtual PS2Request
* allocateRequest();
228 virtual void freeRequest(PS2Request
* request
);
229 virtual bool submitRequest(PS2Request
* request
);
230 virtual void submitRequestAndBlock(PS2Request
* request
);
233 #endif /* _APPLEPS2CONTROLLER_H */