1 /** @file
2   Serial driver that layers on top of a Serial Port Library instance.
3 
4   Copyright (c) 2008 - 2009, Apple Inc. All rights reserved.<BR>
5   Copyright (c) 2013-2014, ARM Ltd. All rights reserved.<BR>
6   Copyright (c) 2015, Intel Corporation. All rights reserved.<BR>
7 
8   SPDX-License-Identifier: BSD-2-Clause-Patent
9 
10 **/
11 
12 #include <Library/UefiBootServicesTableLib.h>
13 #include <Library/SerialPortLib.h>
14 #include <Library/DebugLib.h>
15 #include <Library/PcdLib.h>
16 
17 #include <Protocol/SerialIo.h>
18 #include <Protocol/DevicePath.h>
19 
20 typedef struct {
21   VENDOR_DEVICE_PATH        Guid;
22   UART_DEVICE_PATH          Uart;
23   EFI_DEVICE_PATH_PROTOCOL  End;
24 } SERIAL_DEVICE_PATH;
25 
26 /**
27   Reset the serial device.
28 
29   @param  This              Protocol instance pointer.
30 
31   @retval EFI_SUCCESS       The device was reset.
32   @retval EFI_DEVICE_ERROR  The serial device could not be reset.
33 
34 **/
35 EFI_STATUS
36 EFIAPI
37 SerialReset (
38   IN EFI_SERIAL_IO_PROTOCOL *This
39   );
40 
41 /**
42   Sets the baud rate, receive FIFO depth, transmit/receive time out, parity,
43   data bits, and stop bits on a serial device.
44 
45   @param  This             Protocol instance pointer.
46   @param  BaudRate         The requested baud rate. A BaudRate value of 0 will use the the
47                            device's default interface speed.
48   @param  ReceiveFifoDepth The requested depth of the FIFO on the receive side of the
49                            serial interface. A ReceiveFifoDepth value of 0 will use
50                            the device's default FIFO depth.
51   @param  Timeout          The requested time out for a single character in microseconds.
52                            This timeout applies to both the transmit and receive side of the
53                            interface. A Timeout value of 0 will use the device's default time
54                            out value.
55   @param  Parity           The type of parity to use on this serial device. A Parity value of
56                            DefaultParity will use the device's default parity value.
57   @param  DataBits         The number of data bits to use on the serial device. A DataBits
58                            value of 0 will use the device's default data bit setting.
59   @param  StopBits         The number of stop bits to use on this serial device. A StopBits
60                            value of DefaultStopBits will use the device's default number of
61                            stop bits.
62 
63   @retval EFI_SUCCESS           The device was reset.
64   @retval EFI_INVALID_PARAMETER One or more attributes has an unsupported value.
65   @retval EFI_DEVICE_ERROR      The serial device is not functioning correctly.
66 
67 **/
68 EFI_STATUS
69 EFIAPI
70 SerialSetAttributes (
71   IN EFI_SERIAL_IO_PROTOCOL *This,
72   IN UINT64                 BaudRate,
73   IN UINT32                 ReceiveFifoDepth,
74   IN UINT32                 Timeout,
75   IN EFI_PARITY_TYPE        Parity,
76   IN UINT8                  DataBits,
77   IN EFI_STOP_BITS_TYPE     StopBits
78   );
79 
80 /**
81   Set the control bits on a serial device
82 
83   @param  This             Protocol instance pointer.
84   @param  Control          Set the bits of Control that are settable.
85 
86   @retval EFI_SUCCESS      The new control bits were set on the serial device.
87   @retval EFI_UNSUPPORTED  The serial device does not support this operation.
88   @retval EFI_DEVICE_ERROR The serial device is not functioning correctly.
89 
90 **/
91 EFI_STATUS
92 EFIAPI
93 SerialSetControl (
94   IN EFI_SERIAL_IO_PROTOCOL *This,
95   IN UINT32                 Control
96   );
97 
98 /**
99   Retrieves the status of the control bits on a serial device
100 
101   @param  This              Protocol instance pointer.
102   @param  Control           A pointer to return the current Control signals from the serial device.
103 
104   @retval EFI_SUCCESS       The control bits were read from the serial device.
105   @retval EFI_DEVICE_ERROR  The serial device is not functioning correctly.
106 
107 **/
108 EFI_STATUS
109 EFIAPI
110 SerialGetControl (
111   IN EFI_SERIAL_IO_PROTOCOL *This,
112   OUT UINT32                *Control
113   );
114 
115 /**
116   Writes data to a serial device.
117 
118   @param  This              Protocol instance pointer.
119   @param  BufferSize        On input, the size of the Buffer. On output, the amount of
120                             data actually written.
121   @param  Buffer            The buffer of data to write
122 
123   @retval EFI_SUCCESS       The data was written.
124   @retval EFI_DEVICE_ERROR  The device reported an error.
125   @retval EFI_TIMEOUT       The data write was stopped due to a timeout.
126 
127 **/
128 EFI_STATUS
129 EFIAPI
130 SerialWrite (
131   IN EFI_SERIAL_IO_PROTOCOL *This,
132   IN OUT UINTN              *BufferSize,
133   IN VOID                   *Buffer
134   );
135 
136 /**
137   Reads data from a serial device.
138 
139   @param  This              Protocol instance pointer.
140   @param  BufferSize        On input, the size of the Buffer. On output, the amount of
141                             data returned in Buffer.
142   @param  Buffer            The buffer to return the data into.
143 
144   @retval EFI_SUCCESS       The data was read.
145   @retval EFI_DEVICE_ERROR  The device reported an error.
146   @retval EFI_TIMEOUT       The data write was stopped due to a timeout.
147 
148 **/
149 EFI_STATUS
150 EFIAPI
151 SerialRead (
152   IN EFI_SERIAL_IO_PROTOCOL *This,
153   IN OUT UINTN              *BufferSize,
154   OUT VOID                  *Buffer
155   );
156 
157 EFI_HANDLE mSerialHandle = NULL;
158 
159 SERIAL_DEVICE_PATH mSerialDevicePath = {
160   {
161     { HARDWARE_DEVICE_PATH, HW_VENDOR_DP, { sizeof (VENDOR_DEVICE_PATH), 0} },
162     EFI_CALLER_ID_GUID  // Use the driver's GUID
163   },
164   {
165     { MESSAGING_DEVICE_PATH, MSG_UART_DP, { sizeof (UART_DEVICE_PATH), 0} },
166     0,                  // Reserved
167     0,                  // BaudRate
168     0,                  // DataBits
169     0,                  // Parity
170     0                   // StopBits
171   },
172   { END_DEVICE_PATH_TYPE, END_ENTIRE_DEVICE_PATH_SUBTYPE, { sizeof (EFI_DEVICE_PATH_PROTOCOL), 0 } }
173 };
174 
175 //
176 // Template used to initialize the Serial IO protocols.
177 //
178 EFI_SERIAL_IO_MODE mSerialIoMode = {
179   //
180   //    value  field                set in SerialDxeInitialize()?
181   //---------  -------------------  -----------------------------
182             0, // ControlMask
183   1000 * 1000, // Timeout
184             0, // BaudRate          yes
185             1, // ReceiveFifoDepth
186             0, // DataBits          yes
187             0, // Parity            yes
188             0  // StopBits          yes
189 };
190 
191 EFI_SERIAL_IO_PROTOCOL mSerialIoTemplate = {
192   SERIAL_IO_INTERFACE_REVISION,
193   SerialReset,
194   SerialSetAttributes,
195   SerialSetControl,
196   SerialGetControl,
197   SerialWrite,
198   SerialRead,
199   &mSerialIoMode
200 };
201 
202 /**
203   Reset the serial device.
204 
205   @param  This              Protocol instance pointer.
206 
207   @retval EFI_SUCCESS       The device was reset.
208   @retval EFI_DEVICE_ERROR  The serial device could not be reset.
209 
210 **/
211 EFI_STATUS
212 EFIAPI
SerialReset(IN EFI_SERIAL_IO_PROTOCOL * This)213 SerialReset (
214   IN EFI_SERIAL_IO_PROTOCOL *This
215   )
216 {
217   EFI_STATUS    Status;
218 
219   Status = SerialPortInitialize ();
220   if (EFI_ERROR (Status)) {
221     return Status;
222   }
223 
224   //
225   // Go set the current attributes
226   //
227   Status = This->SetAttributes (
228                    This,
229                    This->Mode->BaudRate,
230                    This->Mode->ReceiveFifoDepth,
231                    This->Mode->Timeout,
232                    (EFI_PARITY_TYPE) This->Mode->Parity,
233                    (UINT8) This->Mode->DataBits,
234                    (EFI_STOP_BITS_TYPE) This->Mode->StopBits
235                    );
236 
237   //
238   // The serial device may not support some of the attributes. To prevent
239   // later failure, always return EFI_SUCCESS when SetAttributes is returning
240   // EFI_INVALID_PARAMETER.
241   //
242   if (Status == EFI_INVALID_PARAMETER) {
243     return EFI_SUCCESS;
244   }
245 
246   return Status;
247 }
248 
249 /**
250   Sets the baud rate, receive FIFO depth, transmit/receive time out, parity,
251   data bits, and stop bits on a serial device.
252 
253   @param  This             Protocol instance pointer.
254   @param  BaudRate         The requested baud rate. A BaudRate value of 0 will use the the
255                            device's default interface speed.
256   @param  ReceiveFifoDepth The requested depth of the FIFO on the receive side of the
257                            serial interface. A ReceiveFifoDepth value of 0 will use
258                            the device's default FIFO depth.
259   @param  Timeout          The requested time out for a single character in microseconds.
260                            This timeout applies to both the transmit and receive side of the
261                            interface. A Timeout value of 0 will use the device's default time
262                            out value.
263   @param  Parity           The type of parity to use on this serial device. A Parity value of
264                            DefaultParity will use the device's default parity value.
265   @param  DataBits         The number of data bits to use on the serial device. A DataBits
266                            value of 0 will use the device's default data bit setting.
267   @param  StopBits         The number of stop bits to use on this serial device. A StopBits
268                            value of DefaultStopBits will use the device's default number of
269                            stop bits.
270 
271   @retval EFI_SUCCESS           The device was reset.
272   @retval EFI_INVALID_PARAMETER One or more attributes has an unsupported value.
273   @retval EFI_DEVICE_ERROR      The serial device is not functioning correctly.
274 
275 **/
276 EFI_STATUS
277 EFIAPI
SerialSetAttributes(IN EFI_SERIAL_IO_PROTOCOL * This,IN UINT64 BaudRate,IN UINT32 ReceiveFifoDepth,IN UINT32 Timeout,IN EFI_PARITY_TYPE Parity,IN UINT8 DataBits,IN EFI_STOP_BITS_TYPE StopBits)278 SerialSetAttributes (
279   IN EFI_SERIAL_IO_PROTOCOL *This,
280   IN UINT64                 BaudRate,
281   IN UINT32                 ReceiveFifoDepth,
282   IN UINT32                 Timeout,
283   IN EFI_PARITY_TYPE        Parity,
284   IN UINT8                  DataBits,
285   IN EFI_STOP_BITS_TYPE     StopBits
286   )
287 {
288   EFI_STATUS                Status;
289   EFI_TPL                   Tpl;
290   UINT64                    OriginalBaudRate;
291   UINT32                    OriginalReceiveFifoDepth;
292   UINT32                    OriginalTimeout;
293   EFI_PARITY_TYPE           OriginalParity;
294   UINT8                     OriginalDataBits;
295   EFI_STOP_BITS_TYPE        OriginalStopBits;
296 
297   //
298   // Preserve the original input values in case
299   // SerialPortSetAttributes() updates the input/output
300   // parameters even on error.
301   //
302   OriginalBaudRate = BaudRate;
303   OriginalReceiveFifoDepth = ReceiveFifoDepth;
304   OriginalTimeout = Timeout;
305   OriginalParity = Parity;
306   OriginalDataBits = DataBits;
307   OriginalStopBits = StopBits;
308   Status = SerialPortSetAttributes (&BaudRate, &ReceiveFifoDepth, &Timeout, &Parity, &DataBits, &StopBits);
309   if (EFI_ERROR (Status)) {
310     //
311     // If it is just to set Timeout value and unsupported is returned,
312     // do not return error.
313     //
314     if ((Status == EFI_UNSUPPORTED) &&
315         (This->Mode->Timeout          != OriginalTimeout) &&
316         (This->Mode->ReceiveFifoDepth == OriginalReceiveFifoDepth) &&
317         (This->Mode->BaudRate         == OriginalBaudRate) &&
318         (This->Mode->DataBits         == (UINT32) OriginalDataBits) &&
319         (This->Mode->Parity           == (UINT32) OriginalParity) &&
320         (This->Mode->StopBits         == (UINT32) OriginalStopBits)) {
321       //
322       // Restore to the original input values.
323       //
324       BaudRate = OriginalBaudRate;
325       ReceiveFifoDepth = OriginalReceiveFifoDepth;
326       Timeout = OriginalTimeout;
327       Parity = OriginalParity;
328       DataBits = OriginalDataBits;
329       StopBits = OriginalStopBits;
330       Status = EFI_SUCCESS;
331     } else if (Status == EFI_INVALID_PARAMETER || Status == EFI_UNSUPPORTED) {
332       return EFI_INVALID_PARAMETER;
333     } else {
334       return EFI_DEVICE_ERROR;
335     }
336   }
337 
338   //
339   // Set the Serial I/O mode and update the device path
340   //
341 
342   Tpl = gBS->RaiseTPL (TPL_NOTIFY);
343 
344   //
345   // Set the Serial I/O mode
346   //
347   This->Mode->ReceiveFifoDepth  = ReceiveFifoDepth;
348   This->Mode->Timeout           = Timeout;
349   This->Mode->BaudRate          = BaudRate;
350   This->Mode->DataBits          = (UINT32) DataBits;
351   This->Mode->Parity            = (UINT32) Parity;
352   This->Mode->StopBits          = (UINT32) StopBits;
353 
354   //
355   // Check if the device path has actually changed
356   //
357   if (mSerialDevicePath.Uart.BaudRate == BaudRate &&
358       mSerialDevicePath.Uart.DataBits == DataBits &&
359       mSerialDevicePath.Uart.Parity   == (UINT8) Parity &&
360       mSerialDevicePath.Uart.StopBits == (UINT8) StopBits
361      ) {
362     gBS->RestoreTPL (Tpl);
363     return EFI_SUCCESS;
364   }
365 
366   //
367   // Update the device path
368   //
369   mSerialDevicePath.Uart.BaudRate = BaudRate;
370   mSerialDevicePath.Uart.DataBits = DataBits;
371   mSerialDevicePath.Uart.Parity   = (UINT8) Parity;
372   mSerialDevicePath.Uart.StopBits = (UINT8) StopBits;
373 
374   Status = gBS->ReinstallProtocolInterface (
375                   mSerialHandle,
376                   &gEfiDevicePathProtocolGuid,
377                   &mSerialDevicePath,
378                   &mSerialDevicePath
379                   );
380 
381   gBS->RestoreTPL (Tpl);
382 
383   return Status;
384 }
385 
386 /**
387   Set the control bits on a serial device
388 
389   @param  This             Protocol instance pointer.
390   @param  Control          Set the bits of Control that are settable.
391 
392   @retval EFI_SUCCESS      The new control bits were set on the serial device.
393   @retval EFI_UNSUPPORTED  The serial device does not support this operation.
394   @retval EFI_DEVICE_ERROR The serial device is not functioning correctly.
395 
396 **/
397 EFI_STATUS
398 EFIAPI
SerialSetControl(IN EFI_SERIAL_IO_PROTOCOL * This,IN UINT32 Control)399 SerialSetControl (
400   IN EFI_SERIAL_IO_PROTOCOL *This,
401   IN UINT32                 Control
402   )
403 {
404   return SerialPortSetControl (Control);
405 }
406 
407 /**
408   Retrieves the status of the control bits on a serial device
409 
410   @param  This              Protocol instance pointer.
411   @param  Control           A pointer to return the current Control signals from the serial device.
412 
413   @retval EFI_SUCCESS       The control bits were read from the serial device.
414   @retval EFI_DEVICE_ERROR  The serial device is not functioning correctly.
415 
416 **/
417 EFI_STATUS
418 EFIAPI
SerialGetControl(IN EFI_SERIAL_IO_PROTOCOL * This,OUT UINT32 * Control)419 SerialGetControl (
420   IN EFI_SERIAL_IO_PROTOCOL *This,
421   OUT UINT32                *Control
422   )
423 {
424   return SerialPortGetControl (Control);
425 }
426 
427 /**
428   Writes data to a serial device.
429 
430   @param  This              Protocol instance pointer.
431   @param  BufferSize        On input, the size of the Buffer. On output, the amount of
432                             data actually written.
433   @param  Buffer            The buffer of data to write
434 
435   @retval EFI_SUCCESS       The data was written.
436   @retval EFI_DEVICE_ERROR  The device reported an error.
437   @retval EFI_TIMEOUT       The data write was stopped due to a timeout.
438 
439 **/
440 EFI_STATUS
441 EFIAPI
SerialWrite(IN EFI_SERIAL_IO_PROTOCOL * This,IN OUT UINTN * BufferSize,IN VOID * Buffer)442 SerialWrite (
443   IN EFI_SERIAL_IO_PROTOCOL *This,
444   IN OUT UINTN              *BufferSize,
445   IN VOID                   *Buffer
446   )
447 {
448   UINTN Count;
449 
450   Count = SerialPortWrite (Buffer, *BufferSize);
451 
452   if (Count != *BufferSize) {
453     *BufferSize = Count;
454     return EFI_TIMEOUT;
455   }
456 
457   return EFI_SUCCESS;
458 }
459 
460 /**
461   Reads data from a serial device.
462 
463   @param  This              Protocol instance pointer.
464   @param  BufferSize        On input, the size of the Buffer. On output, the amount of
465                             data returned in Buffer.
466   @param  Buffer            The buffer to return the data into.
467 
468   @retval EFI_SUCCESS       The data was read.
469   @retval EFI_DEVICE_ERROR  The device reported an error.
470   @retval EFI_TIMEOUT       The data write was stopped due to a timeout.
471 
472 **/
473 EFI_STATUS
474 EFIAPI
SerialRead(IN EFI_SERIAL_IO_PROTOCOL * This,IN OUT UINTN * BufferSize,OUT VOID * Buffer)475 SerialRead (
476   IN EFI_SERIAL_IO_PROTOCOL *This,
477   IN OUT UINTN              *BufferSize,
478   OUT VOID                  *Buffer
479   )
480 {
481   UINTN Count;
482   UINTN TimeOut;
483 
484   Count = 0;
485 
486   while (Count < *BufferSize) {
487     TimeOut = 0;
488     while (TimeOut < mSerialIoMode.Timeout) {
489       if (SerialPortPoll ()) {
490         break;
491       }
492       gBS->Stall (10);
493       TimeOut += 10;
494     }
495     if (TimeOut >= mSerialIoMode.Timeout) {
496       break;
497     }
498     SerialPortRead (Buffer, 1);
499     Count++;
500     Buffer = (VOID *) ((UINT8 *) Buffer + 1);
501   }
502 
503   if (Count != *BufferSize) {
504     *BufferSize = Count;
505     return EFI_TIMEOUT;
506   }
507 
508   return EFI_SUCCESS;
509 }
510 
511 /**
512   Initialization for the Serial Io Protocol.
513 
514   @param[in] ImageHandle    The firmware allocated handle for the EFI image.
515   @param[in] SystemTable    A pointer to the EFI System Table.
516 
517   @retval EFI_SUCCESS       The entry point is executed successfully.
518   @retval other             Some error occurs when executing this entry point.
519 
520 **/
521 EFI_STATUS
522 EFIAPI
SerialDxeInitialize(IN EFI_HANDLE ImageHandle,IN EFI_SYSTEM_TABLE * SystemTable)523 SerialDxeInitialize (
524   IN EFI_HANDLE         ImageHandle,
525   IN EFI_SYSTEM_TABLE   *SystemTable
526   )
527 {
528   EFI_STATUS            Status;
529 
530   mSerialIoMode.BaudRate = PcdGet64 (PcdUartDefaultBaudRate);
531   mSerialIoMode.DataBits = (UINT32) PcdGet8 (PcdUartDefaultDataBits);
532   mSerialIoMode.Parity   = (UINT32) PcdGet8 (PcdUartDefaultParity);
533   mSerialIoMode.StopBits = (UINT32) PcdGet8 (PcdUartDefaultStopBits);
534   mSerialIoMode.ReceiveFifoDepth = PcdGet16 (PcdUartDefaultReceiveFifoDepth);
535   mSerialDevicePath.Uart.BaudRate = PcdGet64 (PcdUartDefaultBaudRate);
536   mSerialDevicePath.Uart.DataBits = PcdGet8 (PcdUartDefaultDataBits);
537   mSerialDevicePath.Uart.Parity   = PcdGet8 (PcdUartDefaultParity);
538   mSerialDevicePath.Uart.StopBits = PcdGet8 (PcdUartDefaultStopBits);
539 
540   //
541   // Issue a reset to initialize the Serial Port
542   //
543   Status = mSerialIoTemplate.Reset (&mSerialIoTemplate);
544   if (EFI_ERROR (Status)) {
545     return Status;
546   }
547 
548   //
549   // Make a new handle with Serial IO protocol and its device path on it.
550   //
551   Status = gBS->InstallMultipleProtocolInterfaces (
552                   &mSerialHandle,
553                   &gEfiSerialIoProtocolGuid,   &mSerialIoTemplate,
554                   &gEfiDevicePathProtocolGuid, &mSerialDevicePath,
555                   NULL
556                   );
557   ASSERT_EFI_ERROR (Status);
558 
559   return Status;
560 }
561 
562