uartpl11.c 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745
  1. /*++
  2. Copyright (c) 2012 Minoca Corp. All Rights Reserved
  3. Module Name:
  4. uartpl11.c
  5. Abstract:
  6. This module implements the kernel serial port interface on a PrimeCell
  7. PL-011 UART.
  8. Author:
  9. Evan Green 7-Aug-2012
  10. Environment:
  11. Kernel
  12. --*/
  13. //
  14. // ------------------------------------------------------------------- Includes
  15. //
  16. //
  17. // Include kernel.h, but be cautious about which APIs are used. Most of the
  18. // system depends on the hardware modules. Limit use to HL, RTL and AR routines.
  19. //
  20. #include <minoca/kernel/kernel.h>
  21. //
  22. // --------------------------------------------------------------------- Macros
  23. //
  24. //
  25. // This macro performs a 32-bit read from the serial port.
  26. //
  27. #define READ_SERIAL_REGISTER(_Register) \
  28. HlReadRegister32(HlPl11UartBase + (_Register))
  29. //
  30. // This macro performs a 32-bit write to the serial port.
  31. //
  32. #define WRITE_SERIAL_REGISTER(_Register, _Value) \
  33. HlWriteRegister32(HlPl11UartBase + (_Register), (_Value))
  34. //
  35. // ---------------------------------------------------------------- Definitions
  36. //
  37. #define UART_CLOCK_FREQUENCY_3MHZ 3000000
  38. #define UART_CLOCK_FREQUENCY_14MHZ 14745600
  39. #define PL11_UART_SIZE 0x1000
  40. //
  41. // Define bits for the PL11 UART Line Control Register.
  42. //
  43. #define PL11_UART_LINE_CONTROL_FIFO_ENABLE 0x10
  44. #define PL11_UART_LINE_CONTROL_WORD_LENGTH_8BITS 0x60
  45. //
  46. // Define bits for the PL11 UART Control Register.
  47. //
  48. #define PL11_UART_CONTROL_UART_ENABLE 0x001
  49. #define PL11_UART_CONTROL_TRANSMITTER_ENABLE 0x100
  50. #define PL11_UART_CONTROL_RECEIVER_ENABLE 0x200
  51. //
  52. // Define the interrupt mask for the UART Interrupt Mask Register.
  53. //
  54. #define PL11_UART_INTERRUPT_MASK 0x7FF
  55. //
  56. // Define bits for the PL11 UART Flags Register.
  57. //
  58. #define PL11_UART_FLAG_CLEAR_TO_SEND 0x001
  59. #define PL11_UART_FLAG_DATA_SET_READY 0x002
  60. #define PL11_UART_FLAG_DATA_CARRIER_DETECT 0x004
  61. #define PL11_UART_FLAG_TRANSMIT_BUSY 0x008
  62. #define PL11_UART_FLAG_RECEIVE_EMPTY 0x010
  63. #define PL11_UART_FLAG_TRANSMIT_FULL 0x020
  64. #define PL11_UART_FLAG_RECEIVE_FULL 0x040
  65. #define PL11_UART_FLAG_TRANSMIT_EMPTY 0x080
  66. #define PL11_UART_FLAG_RING_INDICATOR 0x100
  67. //
  68. // Define bits for the PL11 UART Receive Status register.
  69. //
  70. #define PL11_UART_RECEIVE_STATUS_FRAMING_ERROR 0x0001
  71. #define PL11_UART_RECEIVE_STATUS_PARITY_ERROR 0x0002
  72. #define PL11_UART_RECEIVE_STATUS_BREAK_ERROR 0x0004
  73. #define PL11_UART_RECEIVE_STATUS_OVERRUN_ERROR 0x0008
  74. #define PL11_UART_RECEIVE_STATUS_ERROR_MASK 0x000F
  75. #define PL11_UART_RECEIVE_STATUS_ERROR_CLEAR 0xFF00
  76. //
  77. // Define the bits for the PL11 UART data register.
  78. //
  79. #define PL11_UART_DATA_BYTE_MASK 0x00FF
  80. #define PL11_UART_DATA_FRAMING_ERROR 0x0100
  81. #define PL11_UART_DATA_PARITY_ERROR 0x0200
  82. #define PL11_UART_DATA_BREAK_ERROR 0x0400
  83. #define PL11_UART_DATA_OVERRUN_ERROR 0x0800
  84. #define PL11_UART_DATA_ERROR_MASK 0x0F00
  85. //
  86. // ----------------------------------------------- Internal Function Prototypes
  87. //
  88. KSTATUS
  89. HlpPl11Reset (
  90. PVOID Context,
  91. ULONG BaudRate
  92. );
  93. KSTATUS
  94. HlpPl11Transmit (
  95. PVOID Context,
  96. PVOID Data,
  97. ULONG Size
  98. );
  99. KSTATUS
  100. HlpPl11Receive (
  101. PVOID Context,
  102. PVOID Data,
  103. PULONG Size
  104. );
  105. KSTATUS
  106. HlpPl11GetStatus (
  107. PVOID Context,
  108. PBOOL ReceiveDataAvailable
  109. );
  110. VOID
  111. HlpPl11Disconnect (
  112. PVOID Context
  113. );
  114. //
  115. // ------------------------------------------------------ Data Type Definitions
  116. //
  117. /*++
  118. Structure Description:
  119. This structures defines a baud rate for the PL011 UART.
  120. Members:
  121. BaudRate - Stores the baud rate value.
  122. IntegerDivisor - Stores the integer divisor to program into the PL011.
  123. FractionalDivisor - Stores the fractional divisor to program into the PL011.
  124. --*/
  125. typedef struct _BAUD_RATE {
  126. ULONG BaudRate;
  127. ULONG IntegerDivisor;
  128. ULONG FractionalDivisor;
  129. } BAUD_RATE, *PBAUD_RATE;
  130. //
  131. // Register set definition for the PL-011. These are offsets in bytes, not
  132. // words.
  133. //
  134. typedef enum _PL011_REGISTER {
  135. UartDataBuffer = 0x0,
  136. UartReceiveStatus = 0x4,
  137. UartFlags = 0x18,
  138. UartIrDaLowPowerCounter = 0x20,
  139. UartIntegerBaudRate = 0x24,
  140. UartFractionalBaudRate = 0x28,
  141. UartLineControl = 0x2C,
  142. UartControl = 0x30,
  143. UartFifoInterruptLevel = 0x34,
  144. UartInterruptMask = 0x38,
  145. UartInterruptStatus = 0x3C,
  146. UartMaskedInterrupts = 0x40,
  147. UartInterruptClear = 0x44,
  148. UartDmaControl = 0x48,
  149. UartPeripheralId0 = 0xFE0,
  150. UartPeripheralId1 = 0xFE4,
  151. UartPeripheralId2 = 0xFE8,
  152. UartPeripheralId3 = 0xFEC,
  153. UartPcellId0 = 0xFF0,
  154. UartPcellId1 = 0xFF4,
  155. UartPcellId2 = 0xFF8,
  156. UartPcellId3 = 0xFFC
  157. } PL011_REGISTER, *PPL011_REGISTER;
  158. //
  159. // -------------------------------------------------------------------- Globals
  160. //
  161. //
  162. // Integer and fractional baud rates for an input clock of 14.7456 MHz.
  163. //
  164. BAUD_RATE HlPl11Available14MhzRates[] = {
  165. {9600, 0x60, 0},
  166. {19200, 0x30, 0},
  167. {38400, 0x18, 0},
  168. {57600, 0x10, 0},
  169. {115200, 0x8, 0}
  170. };
  171. //
  172. // Integer and fractional baud rates for an input clock of 3 MHz.
  173. //
  174. BAUD_RATE HlPl11Available3MhzRates[] = {
  175. {9600, 19, 34},
  176. {19200, 9, 49},
  177. {38400, 4, 57},
  178. {57600, 3, 16},
  179. {115200, 1, 40}
  180. };
  181. //
  182. // Store the virtual address of the mapped UART.
  183. //
  184. PVOID HlPl11UartBase = NULL;
  185. //
  186. // Store the physical address of the UART, initialized to a value that should
  187. // never see the light of day unless UART initialization is forced.
  188. //
  189. PHYSICAL_ADDRESS HlPl11UartPhysicalAddress;
  190. //
  191. // Store the clock frequency of the UART, initialized to a value that should
  192. // never see the light of day unless UART initialization is forced.
  193. //
  194. ULONG HlPl11UartClockFrequency;
  195. //
  196. // Store a boolean indicating whether enumeration of this serial port should be
  197. // forced. Setting this to TRUE causes this module to register a serial port
  198. // even if one is not found in firmware tables. This is useful to temporarily
  199. // enable boot debugging on a system.
  200. //
  201. BOOL HlPl11ForceEnumeration = FALSE;
  202. //
  203. // ------------------------------------------------------------------ Functions
  204. //
  205. VOID
  206. HlpPl11SerialModuleEntry (
  207. VOID
  208. )
  209. /*++
  210. Routine Description:
  211. This routine is the entry point for the PL-011 Serial module. Its role is to
  212. detect and report the presence of any PL-011s.
  213. Arguments:
  214. None.
  215. Return Value:
  216. None.
  217. --*/
  218. {
  219. PDEBUG_DEVICE_INFORMATION DebugDevice;
  220. ULONG DebugDeviceIndex;
  221. PDEBUG_PORT_TABLE2 DebugTable;
  222. DEBUG_DEVICE_DESCRIPTION Description;
  223. BOOL FoundIt;
  224. PGENERIC_ADDRESS GenericAddress;
  225. USHORT GenericAddressOffset;
  226. VOID *OemData;
  227. USHORT PortSubType;
  228. USHORT PortType;
  229. KSTATUS Status;
  230. //
  231. // Look for the debug port table.
  232. //
  233. FoundIt = FALSE;
  234. DebugTable = HlGetAcpiTable(DBG2_SIGNATURE, NULL);
  235. if (DebugTable != NULL) {
  236. DebugDevice =
  237. (PDEBUG_DEVICE_INFORMATION)(((PVOID)DebugTable) +
  238. DebugTable->DeviceInformationOffset);
  239. for (DebugDeviceIndex = 0;
  240. DebugDeviceIndex < DebugTable->DeviceInformationCount;
  241. DebugDeviceIndex += 1) {
  242. PortType = READ_UNALIGNED16(&(DebugDevice->PortType));
  243. PortSubType = READ_UNALIGNED16(&(DebugDevice->PortSubType));
  244. if ((PortType == DEBUG_PORT_TYPE_SERIAL) &&
  245. (PortSubType == DEBUG_PORT_SERIAL_ARM_PL011) &&
  246. (DebugDevice->GenericAddressCount == 1) &&
  247. (DebugDevice->OemDataLength == sizeof(ULONG))) {
  248. GenericAddressOffset = READ_UNALIGNED16(
  249. &(DebugDevice->BaseAddressRegisterOffset));
  250. GenericAddress = (PGENERIC_ADDRESS)(((PVOID)DebugDevice) +
  251. GenericAddressOffset);
  252. HlPl11UartPhysicalAddress = GenericAddress->Address;
  253. OemData = ((PVOID)DebugDevice + DebugDevice->OemDataOffset);
  254. HlPl11UartClockFrequency = READ_UNALIGNED32(OemData);
  255. FoundIt = TRUE;
  256. break;
  257. }
  258. DebugDevice = (PDEBUG_DEVICE_INFORMATION)(((PVOID)DebugDevice) +
  259. READ_UNALIGNED16(&(DebugDevice->Length)));
  260. }
  261. }
  262. //
  263. // If no serial port was found and enumeration was not forced, then bail.
  264. //
  265. if ((FoundIt == FALSE) && (HlPl11ForceEnumeration == FALSE)) {
  266. Status = STATUS_SUCCESS;
  267. goto Pl11SerialModuleEntryEnd;
  268. }
  269. //
  270. // Report the physical address space that the UART is occupying.
  271. //
  272. HlReportPhysicalAddressUsage(HlPl11UartPhysicalAddress, PL11_UART_SIZE);
  273. RtlZeroMemory(&Description, sizeof(DEBUG_DEVICE_DESCRIPTION));
  274. Description.TableVersion = DEBUG_DEVICE_DESCRIPTION_VERSION;
  275. Description.FunctionTable.Reset = HlpPl11Reset;
  276. Description.FunctionTable.Transmit = HlpPl11Transmit;
  277. Description.FunctionTable.Receive = HlpPl11Receive;
  278. Description.FunctionTable.GetStatus = HlpPl11GetStatus;
  279. Description.FunctionTable.Disconnect = HlpPl11Disconnect;
  280. Description.PortType = DEBUG_PORT_TYPE_SERIAL;
  281. Description.PortSubType = DEBUG_PORT_SERIAL_ARM_PL011;
  282. Description.Identifier = HlPl11UartPhysicalAddress;
  283. Status = HlRegisterHardware(HardwareModuleDebugDevice, &Description);
  284. if (!KSUCCESS(Status)) {
  285. goto Pl11SerialModuleEntryEnd;
  286. }
  287. Pl11SerialModuleEntryEnd:
  288. return;
  289. }
  290. //
  291. // --------------------------------------------------------- Internal Functions
  292. //
  293. KSTATUS
  294. HlpPl11Reset (
  295. PVOID Context,
  296. ULONG BaudRate
  297. )
  298. /*++
  299. Routine Description:
  300. This routine initializes and resets a debug device, preparing it to send
  301. and receive data.
  302. Arguments:
  303. Context - Supplies the pointer to the port's context, provided by the
  304. hardware module upon initialization.
  305. BaudRate - Supplies the baud rate to set.
  306. Return Value:
  307. STATUS_SUCCESS on success.
  308. Other status codes on failure. The device will not be used if a failure
  309. status code is returned.
  310. --*/
  311. {
  312. ULONG BaudRateCount;
  313. PBAUD_RATE BaudRateData;
  314. PBAUD_RATE BaudRates;
  315. ULONG RateIndex;
  316. KSTATUS Status;
  317. ULONG UartControlValue;
  318. ULONG UartLineControlValue;
  319. BaudRateData = NULL;
  320. switch (HlPl11UartClockFrequency) {
  321. case UART_CLOCK_FREQUENCY_3MHZ:
  322. BaudRates = HlPl11Available3MhzRates;
  323. BaudRateCount = sizeof(HlPl11Available3MhzRates) /
  324. sizeof(HlPl11Available3MhzRates[0]);
  325. break;
  326. case UART_CLOCK_FREQUENCY_14MHZ:
  327. BaudRates = HlPl11Available14MhzRates;
  328. BaudRateCount = sizeof(HlPl11Available14MhzRates) /
  329. sizeof(HlPl11Available14MhzRates[0]);
  330. break;
  331. default:
  332. Status = STATUS_NOT_SUPPORTED;
  333. goto Pl11ResetEnd;
  334. }
  335. for (RateIndex = 0; RateIndex < BaudRateCount; RateIndex += 1) {
  336. BaudRateData = &(BaudRates[RateIndex]);
  337. if (BaudRateData->BaudRate == BaudRate) {
  338. break;
  339. }
  340. }
  341. if (RateIndex == BaudRateCount) {
  342. Status = STATUS_INVALID_CONFIGURATION;
  343. goto Pl11ResetEnd;
  344. }
  345. //
  346. // Map the controller if it has not yet been done.
  347. //
  348. if (HlPl11UartBase == NULL) {
  349. HlPl11UartBase = HlMapPhysicalAddress(HlPl11UartPhysicalAddress,
  350. PL11_UART_SIZE,
  351. TRUE);
  352. if (HlPl11UartBase == NULL) {
  353. Status = STATUS_INSUFFICIENT_RESOURCES;
  354. goto Pl11ResetEnd;
  355. }
  356. }
  357. //
  358. // Program the Control Register. Enable the UART, transmitter, and receiver.
  359. // Clearing the other bits turns off hardware flow control, disables
  360. // loop-back mode, and disables IrDA features.
  361. //
  362. UartControlValue = PL11_UART_CONTROL_UART_ENABLE |
  363. PL11_UART_CONTROL_TRANSMITTER_ENABLE |
  364. PL11_UART_CONTROL_RECEIVER_ENABLE;
  365. WRITE_SERIAL_REGISTER(UartControl, UartControlValue);
  366. //
  367. // Mask all interrupts.
  368. //
  369. WRITE_SERIAL_REGISTER(UartInterruptMask, PL11_UART_INTERRUPT_MASK);
  370. //
  371. // Disable DMA.
  372. //
  373. WRITE_SERIAL_REGISTER(UartDmaControl, 0);
  374. //
  375. // Set the correct divisor values for the chosen baud rate.
  376. //
  377. WRITE_SERIAL_REGISTER(UartIntegerBaudRate, BaudRateData->IntegerDivisor);
  378. WRITE_SERIAL_REGISTER(UartFractionalBaudRate,
  379. BaudRateData->FractionalDivisor);
  380. //
  381. // Program the Line Control Register. Setting bit 4 enables the FIFOs.
  382. // Clearing bit 3 sets 1 stop bit. Clearing bit 1 sets no parity. Clearing
  383. // bit 0 means not sending a break. The TRM for the PL-011 implies that the
  384. // ordering of the Integer Baud Rate, Fractional Baud Rate, and Line Control
  385. // registers is somewhat fixed, so observe that order here.
  386. //
  387. UartLineControlValue = PL11_UART_LINE_CONTROL_FIFO_ENABLE |
  388. PL11_UART_LINE_CONTROL_WORD_LENGTH_8BITS;
  389. WRITE_SERIAL_REGISTER(UartLineControl, UartLineControlValue);
  390. //
  391. // Write a 0 to the receive status register to clear all errors.
  392. //
  393. WRITE_SERIAL_REGISTER(UartReceiveStatus, 0);
  394. Status = STATUS_SUCCESS;
  395. Pl11ResetEnd:
  396. return Status;
  397. }
  398. KSTATUS
  399. HlpPl11Transmit (
  400. PVOID Context,
  401. PVOID Data,
  402. ULONG Size
  403. )
  404. /*++
  405. Routine Description:
  406. This routine transmits data from the host out through the debug device.
  407. Arguments:
  408. Context - Supplies the pointer to the port's context, provided by the
  409. hardware module upon initialization.
  410. Data - Supplies a pointer to the data to write.
  411. Size - Supplies the size to write, in bytes.
  412. Return Value:
  413. STATUS_SUCCESS on success.
  414. STATUS_DEVICE_IO_ERROR if a device error occurred.
  415. --*/
  416. {
  417. ULONG ByteIndex;
  418. PUCHAR Bytes;
  419. Bytes = Data;
  420. for (ByteIndex = 0; ByteIndex < Size; ByteIndex += 1) {
  421. //
  422. // Spin waiting for the buffer to become ready to send. If an error is
  423. // detected, bail out and report to the caller.
  424. //
  425. do {
  426. if ((READ_SERIAL_REGISTER(UartReceiveStatus) &
  427. PL11_UART_RECEIVE_STATUS_ERROR_MASK) != 0) {
  428. return STATUS_DEVICE_IO_ERROR;
  429. }
  430. } while ((READ_SERIAL_REGISTER(UartFlags) &
  431. PL11_UART_FLAG_TRANSMIT_BUSY) != 0);
  432. //
  433. // Send the byte and return.
  434. //
  435. WRITE_SERIAL_REGISTER(UartDataBuffer, Bytes[ByteIndex]);
  436. }
  437. return STATUS_SUCCESS;
  438. }
  439. KSTATUS
  440. HlpPl11Receive (
  441. PVOID Context,
  442. PVOID Data,
  443. PULONG Size
  444. )
  445. /*++
  446. Routine Description:
  447. This routine receives incoming data from the debug device. If no data is
  448. available, this routine should return immediately. If only some of the
  449. requested data is available, this routine should return the data that can
  450. be obtained now and return.
  451. Arguments:
  452. Context - Supplies the pointer to the port's context, provided by the
  453. hardware module upon initialization.
  454. Data - Supplies a pointer where the read data will be returned on success.
  455. Size - Supplies a pointer that on input contains the size of the receive
  456. buffer. On output, returns the number of bytes read.
  457. Return Value:
  458. STATUS_SUCCESS on success.
  459. STATUS_NO_DATA_AVAILABLE if there was no data to be read at the current
  460. time.
  461. STATUS_DEVICE_IO_ERROR if a device error occurred.
  462. --*/
  463. {
  464. ULONG ByteCount;
  465. ULONG ByteIndex;
  466. PUCHAR Bytes;
  467. ULONG DataRegister;
  468. KSTATUS Status;
  469. ByteCount = *Size;
  470. Bytes = Data;
  471. Status = STATUS_NO_DATA_AVAILABLE;
  472. //
  473. // The receive status register contains the break, framing, and parity
  474. // error status for the character read prior to the read of the status. The
  475. // overrun error is set as soon as an overrun occurs. As a result, read the
  476. // data register rather than the status register; the data register also
  477. // returns the status bits.
  478. //
  479. for (ByteIndex = 0; ByteIndex < ByteCount; ByteIndex += 1) {
  480. if ((READ_SERIAL_REGISTER(UartFlags) &
  481. PL11_UART_FLAG_RECEIVE_EMPTY) != 0) {
  482. break;
  483. }
  484. DataRegister = READ_SERIAL_REGISTER(UartDataBuffer);
  485. if ((DataRegister & PL11_UART_DATA_ERROR_MASK) != 0) {
  486. //
  487. // Clear the errors and return.
  488. //
  489. WRITE_SERIAL_REGISTER(UartReceiveStatus,
  490. PL11_UART_RECEIVE_STATUS_ERROR_CLEAR);
  491. Status = STATUS_DEVICE_IO_ERROR;
  492. break;
  493. }
  494. Bytes[ByteIndex] = DataRegister & PL11_UART_DATA_BYTE_MASK;
  495. Status = STATUS_SUCCESS;
  496. }
  497. *Size = ByteIndex;
  498. return Status;
  499. }
  500. KSTATUS
  501. HlpPl11GetStatus (
  502. PVOID Context,
  503. PBOOL ReceiveDataAvailable
  504. )
  505. /*++
  506. Routine Description:
  507. This routine returns the current device status.
  508. Arguments:
  509. Context - Supplies the pointer to the port's context, provided by the
  510. hardware module upon initialization.
  511. ReceiveDataAvailable - Supplies a pointer where a boolean will be returned
  512. indicating whether or not receive data is available.
  513. Return Value:
  514. Status code.
  515. --*/
  516. {
  517. ULONG Flags;
  518. *ReceiveDataAvailable = FALSE;
  519. Flags = READ_SERIAL_REGISTER(UartFlags);
  520. if ((Flags & PL11_UART_FLAG_RECEIVE_EMPTY) == 0) {
  521. *ReceiveDataAvailable = TRUE;
  522. }
  523. return STATUS_SUCCESS;
  524. }
  525. VOID
  526. HlpPl11Disconnect (
  527. PVOID Context
  528. )
  529. /*++
  530. Routine Description:
  531. This routine disconnects a device, taking it offline.
  532. Arguments:
  533. Context - Supplies the pointer to the port's context, provided by the
  534. hardware module upon initialization.
  535. Return Value:
  536. None.
  537. --*/
  538. {
  539. return;
  540. }
  541. //
  542. // --------------------------------------------------------- Internal Functions
  543. //