xref: /reactos/drivers/input/i8042prt/readwrite.c (revision c2c66aff)
1 /*
2  * PROJECT:     ReactOS i8042 (ps/2 keyboard-mouse controller) driver
3  * LICENSE:     GPL - See COPYING in the top level directory
4  * FILE:        drivers/input/i8042prt/readwrite.c
5  * PURPOSE:     Read/write port functions
6  * PROGRAMMERS: Copyright Victor Kirhenshtein (sauros@iname.com)
7                 Copyright Jason Filby (jasonfilby@yahoo.com)
8                 Copyright Martijn Vernooij (o112w8r02@sneakemail.com)
9                 Copyright 2006-2007 Herv� Poussineau (hpoussin@reactos.org)
10  */
11 
12 /* INCLUDES ******************************************************************/
13 
14 #include "i8042prt.h"
15 
16 #include <debug.h>
17 
18 /* FUNCTIONS *****************************************************************/
19 
20 VOID
i8042Flush(IN PPORT_DEVICE_EXTENSION DeviceExtension)21 i8042Flush(
22 	IN PPORT_DEVICE_EXTENSION DeviceExtension)
23 {
24 	UCHAR Ignore;
25 
26 	/* Flush output buffer */
27 	while (NT_SUCCESS(i8042ReadData(DeviceExtension, KBD_OBF /* | MOU_OBF*/, &Ignore))) {
28 		KeStallExecutionProcessor(50);
29 		TRACE_(I8042PRT, "Output data flushed\n");
30 	}
31 
32 	/* Flush input buffer */
33 	while (NT_SUCCESS(i8042ReadData(DeviceExtension, KBD_IBF, &Ignore))) {
34 		KeStallExecutionProcessor(50);
35 		TRACE_(I8042PRT, "Input data flushed\n");
36 	}
37 }
38 
39 BOOLEAN
i8042IsrWritePort(IN PPORT_DEVICE_EXTENSION DeviceExtension,IN UCHAR Value,IN UCHAR SelectCmd OPTIONAL)40 i8042IsrWritePort(
41 	IN PPORT_DEVICE_EXTENSION DeviceExtension,
42 	IN UCHAR Value,
43 	IN UCHAR SelectCmd OPTIONAL)
44 {
45 	if (SelectCmd)
46 		if (!i8042Write(DeviceExtension, DeviceExtension->ControlPort, SelectCmd))
47 			return FALSE;
48 
49 	return i8042Write(DeviceExtension, DeviceExtension->DataPort, Value);
50 }
51 
52 /*
53  * FUNCTION: Read data from port 0x60
54  */
55 NTSTATUS
i8042ReadData(IN PPORT_DEVICE_EXTENSION DeviceExtension,IN UCHAR StatusFlags,OUT PUCHAR Data)56 i8042ReadData(
57 	IN PPORT_DEVICE_EXTENSION DeviceExtension,
58 	IN UCHAR StatusFlags,
59 	OUT PUCHAR Data)
60 {
61 	UCHAR PortStatus;
62 	NTSTATUS Status;
63 
64 	Status = i8042ReadStatus(DeviceExtension, &PortStatus);
65 	if (!NT_SUCCESS(Status))
66 		return Status;
67 
68 	// If data is available
69 	if (PortStatus & StatusFlags)
70 	{
71 		*Data = READ_PORT_UCHAR(DeviceExtension->DataPort);
72 		INFO_(I8042PRT, "Read: 0x%02x (status: 0x%x)\n", Data[0], PortStatus);
73 
74 		// If the data is valid (not timeout, not parity error)
75 		if ((PortStatus & KBD_PERR) == 0)
76 			return STATUS_SUCCESS;
77 	}
78 	return STATUS_UNSUCCESSFUL;
79 }
80 
81 NTSTATUS
i8042ReadStatus(IN PPORT_DEVICE_EXTENSION DeviceExtension,OUT PUCHAR Status)82 i8042ReadStatus(
83 	IN PPORT_DEVICE_EXTENSION DeviceExtension,
84 	OUT PUCHAR Status)
85 {
86 	ASSERT(DeviceExtension->ControlPort != NULL);
87 	*Status = READ_PORT_UCHAR(DeviceExtension->ControlPort);
88 	return STATUS_SUCCESS;
89 }
90 
91 /*
92  * FUNCTION: Read data from data port
93  */
94 NTSTATUS
i8042ReadDataWait(IN PPORT_DEVICE_EXTENSION DeviceExtension,OUT PUCHAR Data)95 i8042ReadDataWait(
96 	IN PPORT_DEVICE_EXTENSION DeviceExtension,
97 	OUT PUCHAR Data)
98 {
99 	ULONG Counter;
100 	NTSTATUS Status;
101 
102 	Counter = DeviceExtension->Settings.PollingIterations;
103 
104 	while (Counter--)
105 	{
106 		Status = i8042ReadKeyboardData(DeviceExtension, Data);
107 
108 		if (NT_SUCCESS(Status))
109 			return Status;
110 
111 		KeStallExecutionProcessor(50);
112 	}
113 
114 	/* Timed out */
115 	return STATUS_IO_TIMEOUT;
116 }
117 
118 /*
119  * This one reads a value from the port; You don't have to specify
120  * which one, it'll always be from the one you talked to, so one function
121  * is enough this time. Note how MSDN specifies the
122  * WaitForAck parameter to be ignored.
123  */
124 NTSTATUS NTAPI
i8042SynchReadPort(IN PVOID Context,OUT PUCHAR Value,IN BOOLEAN WaitForAck)125 i8042SynchReadPort(
126 	IN PVOID Context,
127 	OUT PUCHAR Value,
128 	IN BOOLEAN WaitForAck)
129 {
130 	PPORT_DEVICE_EXTENSION DeviceExtension;
131 
132 	UNREFERENCED_PARAMETER(WaitForAck);
133 
134 	DeviceExtension = (PPORT_DEVICE_EXTENSION)Context;
135 
136 	return i8042ReadDataWait(DeviceExtension, Value);
137 }
138 
139 /*
140  * These functions are callbacks for filter driver custom
141  * initialization routines.
142  */
143 NTSTATUS NTAPI
i8042SynchWritePort(IN PPORT_DEVICE_EXTENSION DeviceExtension,IN UCHAR Port,IN UCHAR Value,IN BOOLEAN WaitForAck)144 i8042SynchWritePort(
145 	IN PPORT_DEVICE_EXTENSION DeviceExtension,
146 	IN UCHAR Port,
147 	IN UCHAR Value,
148 	IN BOOLEAN WaitForAck)
149 {
150 	NTSTATUS Status;
151 	UCHAR Ack;
152 	ULONG ResendIterations;
153 
154 	ResendIterations = DeviceExtension->Settings.ResendIterations + 1;
155 
156 	do
157 	{
158 		if (Port)
159 			if (!i8042Write(DeviceExtension, DeviceExtension->DataPort, Port))
160 			{
161 				WARN_(I8042PRT, "Failed to write Port\n");
162 				return STATUS_IO_TIMEOUT;
163 			}
164 
165 		if (!i8042Write(DeviceExtension, DeviceExtension->DataPort, Value))
166 		{
167 			WARN_(I8042PRT, "Failed to write Value\n");
168 			return STATUS_IO_TIMEOUT;
169 		}
170 
171 		if (WaitForAck)
172 		{
173 			Status = i8042ReadDataWait(DeviceExtension, &Ack);
174 			if (!NT_SUCCESS(Status))
175 			{
176 				WARN_(I8042PRT, "Failed to read Ack\n");
177 				return Status;
178 			}
179 			if (Ack == KBD_ACK)
180 				return STATUS_SUCCESS;
181 			else if (Ack == KBD_RESEND)
182 				INFO_(I8042PRT, "i8042 asks for a data resend\n");
183 		}
184 		else
185 		{
186 			return STATUS_SUCCESS;
187 		}
188 		TRACE_(I8042PRT, "Reiterating\n");
189 		ResendIterations--;
190 	} while (ResendIterations);
191 
192 	return STATUS_IO_TIMEOUT;
193 }
194 
195 /*
196  * FUNCTION: Write data to a port, waiting first for it to become ready
197  */
198 BOOLEAN
i8042Write(IN PPORT_DEVICE_EXTENSION DeviceExtension,IN PUCHAR addr,IN UCHAR data)199 i8042Write(
200 	IN PPORT_DEVICE_EXTENSION DeviceExtension,
201 	IN PUCHAR addr,
202 	IN UCHAR data)
203 {
204 	ULONG Counter;
205 
206 	ASSERT(addr);
207 	ASSERT(DeviceExtension->ControlPort != NULL);
208 
209 	Counter = DeviceExtension->Settings.PollingIterations;
210 
211 	while ((KBD_IBF & READ_PORT_UCHAR(DeviceExtension->ControlPort)) &&
212 	       (Counter--))
213 	{
214 		KeStallExecutionProcessor(50);
215 	}
216 
217 	if (Counter)
218 	{
219 		WRITE_PORT_UCHAR(addr, data);
220 		INFO_(I8042PRT, "Sent 0x%x to port %p\n", data, addr);
221 		return TRUE;
222 	}
223 	return FALSE;
224 }
225