xref: /reactos/drivers/base/kdcom/kdcom.c (revision 5100859e)
1 /*
2  * COPYRIGHT:       GPL, see COPYING in the top level directory
3  * PROJECT:         ReactOS kernel
4  * FILE:            drivers/base/kddll/kdcom.c
5  * PURPOSE:         COM port functions for the kernel debugger.
6  * PROGRAMMER:      Timo Kreuzer (timo.kreuzer@reactos.org)
7  */
8 
9 #include "kddll.h"
10 
11 #include <cportlib/cportlib.h>
12 #include <arc/arc.h>
13 #include <stdio.h>
14 #include <stdlib.h>
15 #include <ndk/halfuncs.h>
16 
17 /* Serial debug connection */
18 #define DEFAULT_DEBUG_PORT      2 /* COM2 */
19 #define DEFAULT_DEBUG_COM1_IRQ  4 /* COM1 IRQ */
20 #define DEFAULT_DEBUG_COM2_IRQ  3 /* COM2 IRQ */
21 #define DEFAULT_DEBUG_BAUD_RATE 115200 /* 115200 Baud */
22 
23 #define DEFAULT_BAUD_RATE   19200
24 
25 #if defined(_M_IX86) || defined(_M_AMD64)
26 const ULONG BaseArray[] = {0, 0x3F8, 0x2F8, 0x3E8, 0x2E8};
27 #elif defined(_M_PPC)
28 const ULONG BaseArray[] = {0, 0x800003F8};
29 #elif defined(_M_MIPS)
30 const ULONG BaseArray[] = {0, 0x80006000, 0x80007000};
31 #elif defined(_M_ARM)
32 const ULONG BaseArray[] = {0, 0xF1012000};
33 #else
34 #error Unknown architecture
35 #endif
36 
37 #define MAX_COM_PORTS   (sizeof(BaseArray) / sizeof(BaseArray[0]) - 1)
38 
39 /* GLOBALS ********************************************************************/
40 
41 CPPORT KdComPort;
42 ULONG  KdComPortIrq = 0; // Not used at the moment.
43 #ifdef KDDEBUG
44 CPPORT KdDebugComPort;
45 #endif
46 
47 /* DEBUGGING ******************************************************************/
48 
49 #ifdef KDDEBUG
50 ULONG KdpDbgPrint(const char *Format, ...)
51 {
52     va_list ap;
53     int Length;
54     char* ptr;
55     CHAR Buffer[512];
56 
57     va_start(ap, Format);
58     Length = _vsnprintf(Buffer, sizeof(Buffer), Format, ap);
59     va_end(ap);
60 
61     /* Check if we went past the buffer */
62     if (Length == -1)
63     {
64         /* Terminate it if we went over-board */
65         Buffer[sizeof(Buffer) - 1] = '\n';
66 
67         /* Put maximum */
68         Length = sizeof(Buffer);
69     }
70 
71     ptr = Buffer;
72     while (Length--)
73     {
74         if (*ptr == '\n')
75             CpPutByte(&KdDebugComPort, '\r');
76 
77         CpPutByte(&KdDebugComPort, *ptr++);
78     }
79 
80     return 0;
81 }
82 #endif
83 
84 /* FUNCTIONS ******************************************************************/
85 
86 NTSTATUS
87 NTAPI
88 KdD0Transition(VOID)
89 {
90     return STATUS_SUCCESS;
91 }
92 
93 NTSTATUS
94 NTAPI
95 KdD3Transition(VOID)
96 {
97     return STATUS_SUCCESS;
98 }
99 
100 NTSTATUS
101 NTAPI
102 KdSave(IN BOOLEAN SleepTransition)
103 {
104     /* Nothing to do on COM ports */
105     return STATUS_SUCCESS;
106 }
107 
108 NTSTATUS
109 NTAPI
110 KdRestore(IN BOOLEAN SleepTransition)
111 {
112     /* Nothing to do on COM ports */
113     return STATUS_SUCCESS;
114 }
115 
116 NTSTATUS
117 NTAPI
118 KdpPortInitialize(IN ULONG ComPortNumber,
119                   IN ULONG ComPortBaudRate)
120 {
121     NTSTATUS Status;
122 
123     KDDBGPRINT("KdpPortInitialize, Port = COM%ld\n", ComPortNumber);
124 
125     Status = CpInitialize(&KdComPort,
126                           UlongToPtr(BaseArray[ComPortNumber]),
127                           ComPortBaudRate);
128     if (!NT_SUCCESS(Status))
129     {
130         return STATUS_INVALID_PARAMETER;
131     }
132     else
133     {
134         KdComPortInUse = KdComPort.Address;
135         return STATUS_SUCCESS;
136     }
137 }
138 
139 /******************************************************************************
140  * \name KdDebuggerInitialize0
141  * \brief Phase 0 initialization.
142  * \param [opt] LoaderBlock Pointer to the Loader parameter block. Can be NULL.
143  * \return Status
144  */
145 NTSTATUS
146 NTAPI
147 KdDebuggerInitialize0(IN PLOADER_PARAMETER_BLOCK LoaderBlock OPTIONAL)
148 {
149     ULONG ComPortNumber   = DEFAULT_DEBUG_PORT;
150     ULONG ComPortBaudRate = DEFAULT_DEBUG_BAUD_RATE;
151 
152     PCHAR CommandLine, PortString, BaudString, IrqString;
153     ULONG Value;
154 
155     /* Check if we have a LoaderBlock */
156     if (LoaderBlock)
157     {
158         /* Get the Command Line */
159         CommandLine = LoaderBlock->LoadOptions;
160 
161         /* Upcase it */
162         _strupr(CommandLine);
163 
164         /* Get the port and baud rate */
165         PortString = strstr(CommandLine, "DEBUGPORT");
166         BaudString = strstr(CommandLine, "BAUDRATE");
167         IrqString  = strstr(CommandLine, "IRQ");
168 
169         /* Check if we got the /DEBUGPORT parameter */
170         if (PortString)
171         {
172             /* Move past the actual string, to reach the port*/
173             PortString += strlen("DEBUGPORT");
174 
175             /* Now get past any spaces and skip the equal sign */
176             while (*PortString == ' ') PortString++;
177             PortString++;
178 
179             /* Do we have a serial port? */
180             if (strncmp(PortString, "COM", 3) != 0)
181             {
182                 return STATUS_INVALID_PARAMETER;
183             }
184 
185             /* Check for a valid Serial Port */
186             PortString += 3;
187             Value = atol(PortString);
188             if (Value >= sizeof(BaseArray) / sizeof(BaseArray[0]))
189             {
190                 return STATUS_INVALID_PARAMETER;
191             }
192 
193             /* Set the port to use */
194             ComPortNumber = Value;
195        }
196 
197         /* Check if we got a baud rate */
198         if (BaudString)
199         {
200             /* Move past the actual string, to reach the rate */
201             BaudString += strlen("BAUDRATE");
202 
203             /* Now get past any spaces */
204             while (*BaudString == ' ') BaudString++;
205 
206             /* And make sure we have a rate */
207             if (*BaudString)
208             {
209                 /* Read and set it */
210                 Value = atol(BaudString + 1);
211                 if (Value) ComPortBaudRate = Value;
212             }
213         }
214 
215         /* Check Serial Port Settings [IRQ] */
216         if (IrqString)
217         {
218             /* Move past the actual string, to reach the rate */
219             IrqString += strlen("IRQ");
220 
221             /* Now get past any spaces */
222             while (*IrqString == ' ') IrqString++;
223 
224             /* And make sure we have an IRQ */
225             if (*IrqString)
226             {
227                 /* Read and set it */
228                 Value = atol(IrqString + 1);
229                 if (Value) KdComPortIrq = Value;
230             }
231         }
232     }
233 
234 #ifdef KDDEBUG
235     /*
236      * Try to find a free COM port and use it as the KD debugging port.
237      * NOTE: Inspired by reactos/boot/freeldr/freeldr/comm/rs232.c, Rs232PortInitialize(...)
238      */
239     {
240     /*
241      * Start enumerating COM ports from the last one to the first one,
242      * and break when we find a valid port.
243      * If we reach the first element of the list, the invalid COM port,
244      * then it means that no valid port was found.
245      */
246     ULONG ComPort;
247     for (ComPort = MAX_COM_PORTS; ComPort > 0; ComPort--)
248     {
249         /* Check if the port exist; skip the KD port */
250         if ((ComPort != ComPortNumber) && CpDoesPortExist(UlongToPtr(BaseArray[ComPort])))
251             break;
252     }
253     if (ComPort != 0)
254         CpInitialize(&KdDebugComPort, UlongToPtr(BaseArray[ComPort]), DEFAULT_BAUD_RATE);
255     }
256 #endif
257 
258     KDDBGPRINT("KdDebuggerInitialize0\n");
259 
260     /* Initialize the port */
261     return KdpPortInitialize(ComPortNumber, ComPortBaudRate);
262 }
263 
264 /******************************************************************************
265  * \name KdDebuggerInitialize1
266  * \brief Phase 1 initialization.
267  * \param [opt] LoaderBlock Pointer to the Loader parameter block. Can be NULL.
268  * \return Status
269  */
270 NTSTATUS
271 NTAPI
272 KdDebuggerInitialize1(IN PLOADER_PARAMETER_BLOCK LoaderBlock OPTIONAL)
273 {
274     return STATUS_SUCCESS;
275 }
276 
277 
278 VOID
279 NTAPI
280 KdpSendByte(IN UCHAR Byte)
281 {
282     /* Send the byte */
283     CpPutByte(&KdComPort, Byte);
284 }
285 
286 KDP_STATUS
287 NTAPI
288 KdpPollByte(OUT PUCHAR OutByte)
289 {
290     USHORT Status;
291 
292     /* Poll the byte */
293     Status = CpGetByte(&KdComPort, OutByte, FALSE, FALSE);
294     switch (Status)
295     {
296         case CP_GET_SUCCESS:
297             return KDP_PACKET_RECEIVED;
298 
299         case CP_GET_NODATA:
300             return KDP_PACKET_TIMEOUT;
301 
302         case CP_GET_ERROR:
303         default:
304             return KDP_PACKET_RESEND;
305     }
306 }
307 
308 KDP_STATUS
309 NTAPI
310 KdpReceiveByte(OUT PUCHAR OutByte)
311 {
312     USHORT Status;
313 
314     /* Get the byte */
315     Status = CpGetByte(&KdComPort, OutByte, TRUE, FALSE);
316     switch (Status)
317     {
318         case CP_GET_SUCCESS:
319             return KDP_PACKET_RECEIVED;
320 
321         case CP_GET_NODATA:
322             return KDP_PACKET_TIMEOUT;
323 
324         case CP_GET_ERROR:
325         default:
326             return KDP_PACKET_RESEND;
327     }
328 }
329 
330 KDP_STATUS
331 NTAPI
332 KdpPollBreakIn(VOID)
333 {
334     KDP_STATUS KdStatus;
335     UCHAR Byte;
336 
337     KdStatus = KdpPollByte(&Byte);
338     if ((KdStatus == KDP_PACKET_RECEIVED) && (Byte == BREAKIN_PACKET_BYTE))
339     {
340         return KDP_PACKET_RECEIVED;
341     }
342     return KDP_PACKET_TIMEOUT;
343 }
344 
345 /* EOF */
346