ns16550.c 23 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047
  1. /*++
  2. Copyright (c) 2012 Minoca Corp. All Rights Reserved
  3. Module Name:
  4. ns16550.c
  5. Abstract:
  6. This module implements the kernel serial port interface on a 16550 standard
  7. 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. #include <minoca/kernel/ioport.h>
  22. //
  23. // --------------------------------------------------------------------- Macros
  24. //
  25. //
  26. // Macros to read from and write to 16550 registers.
  27. //
  28. #define NS16550_READ8(_Device, _Register) \
  29. (_Device)->Read8((_Device), (_Register))
  30. #define NS16550_WRITE8(_Device, _Register, _Value) \
  31. (_Device)->Write8((_Device), (_Register), (_Value))
  32. //
  33. // This macro returns the offset of a given register from its base.
  34. //
  35. #define NS16550_REGISTER_OFFSET(_Device, _Register) \
  36. ((_Device)->RegisterOffset + ((_Register) << (_Device)->RegisterShift))
  37. //
  38. // ---------------------------------------------------------------- Definitions
  39. //
  40. #define NS16550_ALLOCATION_TAG 0x3631534E
  41. //
  42. // If forced, define the port number to assume the serial port is at.
  43. //
  44. #define NS16550_DEFAULT_IO_PORT_BASE 0x3F8
  45. #define NS16550_DEFAULT_BASE_BAUD 115200
  46. //
  47. // Define the bits for the PC UART Line Status register.
  48. //
  49. #define NS16550_LINE_STATUS_DATA_READY 0x01
  50. #define NS16550_LINE_STATUS_TRANSMIT_EMPTY 0x20
  51. #define NS16550_LINE_STATUS_ERRORS 0x8E
  52. //
  53. // Define the possible register shift values.
  54. //
  55. #define NS16550_1_BYTE_REGISTER_SHIFT 0
  56. #define NS16550_2_BYTE_REGISTER_SHIFT 1
  57. #define NS16550_4_BYTE_REGISTER_SHIFT 2
  58. //
  59. // ------------------------------------------------------ Data Type Definitions
  60. //
  61. typedef enum _NS16550_REGISTER {
  62. Ns16550Data = 0,
  63. Ns16550DivisorLow = 0,
  64. Ns16550InterruptEnable = 1,
  65. Ns16550DivisorHigh = 1,
  66. Ns16550InterruptStatus = 2,
  67. Ns16550FifoControl = 2,
  68. Ns16550LineControl = 3,
  69. Ns16550ModemControl = 4,
  70. Ns16550LineStatus = 5,
  71. Ns16550ModemStatus = 6,
  72. Ns16550Scratch = 7
  73. } NS16550_REGISTER, *PNS16550_REGISTER;
  74. typedef struct _NS16550 NS16550, *PNS16550;
  75. typedef
  76. UCHAR
  77. (*PNS16550_READ8) (
  78. PNS16550 Device,
  79. NS16550_REGISTER Register
  80. );
  81. /*++
  82. Routine Description:
  83. This routine reads a 16550 register.
  84. Arguments:
  85. Device - Supplies a pointer to the device context.
  86. Register - Supplies the register to read.
  87. Return Value:
  88. Returns the value at the register.
  89. --*/
  90. typedef
  91. VOID
  92. (*PNS16550_WRITE8) (
  93. PNS16550 Device,
  94. NS16550_REGISTER Register,
  95. UCHAR Value
  96. );
  97. /*++
  98. Routine Description:
  99. This routine writes to a 16550 register.
  100. Arguments:
  101. Device - Supplies a pointer to the device context.
  102. Register - Supplies the register to write to.
  103. Value - Supplies the value to write.
  104. Return Value:
  105. None.
  106. --*/
  107. /*++
  108. Structure Description:
  109. This structure defines the context for a 16550 UART.
  110. Members:
  111. MemoryBase - Stores a pointer to the virtual address of the registers, if
  112. the registers are memory mapped. This contains NULL for I/O port
  113. implementations.
  114. IoBase - Stores the I/O port base of the registers if they are accessed via
  115. I/O ports.
  116. RegisterOffset - Stores the offset in bytes from the start of the register
  117. base to the 16550 registers.
  118. RegisterShift - Stores the amount to shift the register number by to get
  119. the real register.
  120. BaseBaud - Stores the base baud rate for a divisor value of 1.
  121. Flags - Stores the bitmask of flags. See DEBUG_PORT_16550_OEM_FLAG_* for
  122. definitions.
  123. PhysicalMemoryBase - Stores the physical address
  124. RegionSize - Stores the size of the region.
  125. Read8 - Stores a pointer to a function used to read from the registers.
  126. Write8 - Stores a pointer to a functino used to write to the registers.
  127. --*/
  128. struct _NS16550 {
  129. PVOID MemoryBase;
  130. USHORT IoBase;
  131. UINTN RegisterOffset;
  132. ULONG RegisterShift;
  133. ULONG BaseBaud;
  134. ULONG Flags;
  135. PHYSICAL_ADDRESS PhysicalMemoryBase;
  136. UINTN RegionSize;
  137. PNS16550_READ8 Read8;
  138. PNS16550_WRITE8 Write8;
  139. };
  140. //
  141. // ----------------------------------------------- Internal Function Prototypes
  142. //
  143. KSTATUS
  144. HlpNs16550RegisterDevice (
  145. USHORT IoPortBase,
  146. PHYSICAL_ADDRESS PhysicalBase,
  147. ULONG Size,
  148. PDEBUG_PORT_16550_OEM_DATA OemData
  149. );
  150. KSTATUS
  151. HlpNs16550Reset (
  152. PVOID Context,
  153. ULONG BaudRate
  154. );
  155. KSTATUS
  156. HlpNs16550Transmit (
  157. PVOID Context,
  158. PVOID Data,
  159. ULONG Size
  160. );
  161. KSTATUS
  162. HlpNs16550Receive (
  163. PVOID Context,
  164. PVOID Data,
  165. PULONG Size
  166. );
  167. KSTATUS
  168. HlpNs16550GetStatus (
  169. PVOID Context,
  170. PBOOL ReceiveDataAvailable
  171. );
  172. VOID
  173. HlpNs16550Disconnect (
  174. PVOID Context
  175. );
  176. UCHAR
  177. HlpNs16550ReadIo8 (
  178. PNS16550 Device,
  179. NS16550_REGISTER Register
  180. );
  181. VOID
  182. HlpNs16550WriteIo8 (
  183. PNS16550 Device,
  184. NS16550_REGISTER Register,
  185. UCHAR Value
  186. );
  187. UCHAR
  188. HlpNs16550ReadMemory8 (
  189. PNS16550 Device,
  190. NS16550_REGISTER Register
  191. );
  192. VOID
  193. HlpNs16550WriteMemory8 (
  194. PNS16550 Device,
  195. NS16550_REGISTER Register,
  196. UCHAR Value
  197. );
  198. //
  199. // -------------------------------------------------------------------- Globals
  200. //
  201. //
  202. // Define a boolean that can be set to force the UART to enumerate.
  203. //
  204. BOOL HlNs16550ForceEnumeration = FALSE;
  205. //
  206. // Define a boolean that be set to force the UART to never enumerate.
  207. //
  208. BOOL HlNs16550ForceNoEnumeration = FALSE;
  209. //
  210. // ------------------------------------------------------------------ Functions
  211. //
  212. VOID
  213. HlpNs16550SerialModuleEntry (
  214. VOID
  215. )
  216. /*++
  217. Routine Description:
  218. This routine is the entry point for the PC Serial module. Its role is to
  219. detect and report the presence of a PC Serial port module.
  220. Arguments:
  221. None.
  222. Return Value:
  223. None.
  224. --*/
  225. {
  226. PDEBUG_PORT_TABLE2 DebugTable;
  227. PDEBUG_DEVICE_INFORMATION Device;
  228. ULONG DeviceIndex;
  229. BOOL FoundDevice;
  230. PGENERIC_ADDRESS GenericAddress;
  231. ULONG IoPortBase;
  232. PDEBUG_PORT_16550_OEM_DATA OemData;
  233. PHYSICAL_ADDRESS PhysicalBase;
  234. UINTN RegionSize;
  235. PULONG SizePointer;
  236. KSTATUS Status;
  237. if (HlNs16550ForceNoEnumeration != FALSE) {
  238. return;
  239. }
  240. FoundDevice = FALSE;
  241. DebugTable = HlGetAcpiTable(DBG2_SIGNATURE, NULL);
  242. if (DebugTable != NULL) {
  243. //
  244. // Loop through looking for 16550 debug devices.
  245. //
  246. Device =
  247. (PDEBUG_DEVICE_INFORMATION)((PVOID)DebugTable +
  248. DebugTable->DeviceInformationOffset);
  249. SizePointer = (PULONG)((PVOID)Device + Device->AddressSizeOffset);
  250. for (DeviceIndex = 0;
  251. DeviceIndex < DebugTable->DeviceInformationCount;
  252. DeviceIndex += 1) {
  253. IoPortBase = 0;
  254. PhysicalBase = 0;
  255. OemData = NULL;
  256. RegionSize = 0;
  257. if ((Device->PortType == DEBUG_PORT_TYPE_SERIAL) &&
  258. ((Device->PortSubType == DEBUG_PORT_SERIAL_16550) ||
  259. (Device->PortSubType == DEBUG_PORT_SERIAL_16550_COMPATIBLE))) {
  260. if ((Device->OemDataOffset != 0) &&
  261. (Device->OemDataLength >=
  262. sizeof(DEBUG_PORT_16550_OEM_DATA))) {
  263. OemData = (PDEBUG_PORT_16550_OEM_DATA)((PUCHAR)Device +
  264. Device->OemDataOffset);
  265. }
  266. if (Device->GenericAddressCount >= 1) {
  267. GenericAddress =
  268. (PGENERIC_ADDRESS)((PVOID)Device +
  269. Device->BaseAddressRegisterOffset);
  270. if (GenericAddress->AddressSpaceId == AddressSpaceMemory) {
  271. PhysicalBase = GenericAddress->Address;
  272. RegionSize = *SizePointer;
  273. FoundDevice = TRUE;
  274. IoPortBase = 0;
  275. } else if (GenericAddress->AddressSpaceId ==
  276. AddressSpaceIo) {
  277. IoPortBase = GenericAddress->Address;
  278. RegionSize = *SizePointer;
  279. FoundDevice = TRUE;
  280. }
  281. Status = HlpNs16550RegisterDevice(IoPortBase,
  282. PhysicalBase,
  283. RegionSize,
  284. OemData);
  285. if (!KSUCCESS(Status)) {
  286. goto Ns16550ModuleEntryEnd;
  287. }
  288. FoundDevice = TRUE;
  289. }
  290. }
  291. Device = (PDEBUG_DEVICE_INFORMATION)((PVOID)Device +
  292. READ_UNALIGNED16(&(Device->Length)));
  293. }
  294. }
  295. //
  296. // If no device was found and enumeration was not forced, return.
  297. //
  298. if ((FoundDevice != FALSE) || (HlNs16550ForceEnumeration == FALSE)) {
  299. return;
  300. }
  301. //
  302. // Enumerate a forced serial device.
  303. //
  304. IoPortBase = NS16550_DEFAULT_IO_PORT_BASE;
  305. Status = HlpNs16550RegisterDevice(IoPortBase, 0, 0, NULL);
  306. if (!KSUCCESS(Status)) {
  307. goto Ns16550ModuleEntryEnd;
  308. }
  309. Ns16550ModuleEntryEnd:
  310. return;
  311. }
  312. //
  313. // --------------------------------------------------------- Internal Functions
  314. //
  315. KSTATUS
  316. HlpNs16550RegisterDevice (
  317. USHORT IoPortBase,
  318. PHYSICAL_ADDRESS PhysicalBase,
  319. ULONG Size,
  320. PDEBUG_PORT_16550_OEM_DATA OemData
  321. )
  322. /*++
  323. Routine Description:
  324. This routine registers a 16550 UART debug device.
  325. Arguments:
  326. IoPortBase - Supplies the I/O port base of the UART, if it is based in
  327. I/O port space.
  328. PhysicalBase - Supplies the physical base address of the UART if it is
  329. memory mapped, or 0 if the device is in I/O port space.
  330. Size - Supplies the size of the region, in bytes.
  331. OemData - Supplies an optional pointer to the OEM data.
  332. Return Value:
  333. Status code.
  334. --*/
  335. {
  336. DEBUG_DEVICE_DESCRIPTION DebugDevice;
  337. PNS16550 DeviceContext;
  338. KSTATUS Status;
  339. //
  340. // Allocate the context and fill it in.
  341. //
  342. DeviceContext = HlAllocateMemory(sizeof(NS16550),
  343. NS16550_ALLOCATION_TAG,
  344. FALSE,
  345. NULL);
  346. if (DeviceContext == NULL) {
  347. Status = STATUS_INSUFFICIENT_RESOURCES;
  348. goto Ns16550RegisterDeviceEnd;
  349. }
  350. RtlZeroMemory(DeviceContext, sizeof(NS16550));
  351. DeviceContext->PhysicalMemoryBase = PhysicalBase;
  352. DeviceContext->IoBase = IoPortBase;
  353. DeviceContext->BaseBaud = NS16550_DEFAULT_BASE_BAUD;
  354. DeviceContext->RegionSize = Size;
  355. //
  356. // Use the OEM data if it's valid.
  357. //
  358. if ((OemData != NULL) &&
  359. (OemData->Signature == DEBUG_PORT_16550_OEM_DATA_SIGNATURE)) {
  360. DeviceContext->RegisterOffset = OemData->RegisterOffset;
  361. DeviceContext->RegisterShift = OemData->RegisterShift;
  362. DeviceContext->BaseBaud = OemData->BaseBaud;
  363. DeviceContext->Flags = OemData->Flags;
  364. }
  365. //
  366. // Register the serial port.
  367. //
  368. RtlZeroMemory(&DebugDevice, sizeof(DEBUG_DEVICE_DESCRIPTION));
  369. DebugDevice.TableVersion = DEBUG_DEVICE_DESCRIPTION_VERSION;
  370. DebugDevice.Context = DeviceContext;
  371. DebugDevice.FunctionTable.Reset = HlpNs16550Reset;
  372. DebugDevice.FunctionTable.Transmit = HlpNs16550Transmit;
  373. DebugDevice.FunctionTable.Receive = HlpNs16550Receive;
  374. DebugDevice.FunctionTable.GetStatus = HlpNs16550GetStatus;
  375. DebugDevice.FunctionTable.Disconnect = HlpNs16550Disconnect;
  376. DebugDevice.PortType = DEBUG_PORT_TYPE_SERIAL;
  377. DebugDevice.PortSubType = DEBUG_PORT_SERIAL_16550_COMPATIBLE;
  378. if (PhysicalBase != 0) {
  379. DebugDevice.Identifier = PhysicalBase;
  380. } else {
  381. DebugDevice.Identifier = IoPortBase;
  382. }
  383. Status = HlRegisterHardware(HardwareModuleDebugDevice, &DebugDevice);
  384. if (!KSUCCESS(Status)) {
  385. goto Ns16550RegisterDeviceEnd;
  386. }
  387. Ns16550RegisterDeviceEnd:
  388. return Status;
  389. }
  390. KSTATUS
  391. HlpNs16550Reset (
  392. PVOID Context,
  393. ULONG BaudRate
  394. )
  395. /*++
  396. Routine Description:
  397. This routine initializes and resets a debug device, preparing it to send
  398. and receive data.
  399. Arguments:
  400. Context - Supplies the pointer to the port's context, provided by the
  401. hardware module upon initialization.
  402. BaudRate - Supplies the baud rate to set.
  403. Return Value:
  404. STATUS_SUCCESS on success.
  405. Other status codes on failure. The device will not be used if a failure
  406. status code is returned.
  407. --*/
  408. {
  409. ULONG CurrentBaud;
  410. PNS16550 Device;
  411. ULONG Divisor;
  412. UCHAR Value;
  413. Device = Context;
  414. //
  415. // Compute the baud rate divisor.
  416. //
  417. if (BaudRate > Device->BaseBaud) {
  418. return STATUS_NOT_SUPPORTED;
  419. }
  420. Divisor = 1;
  421. while (TRUE) {
  422. CurrentBaud = Device->BaseBaud / Divisor;
  423. if ((CurrentBaud <= BaudRate) || (CurrentBaud == 0)) {
  424. break;
  425. }
  426. Divisor += 1;
  427. }
  428. if ((CurrentBaud == 0) || (Divisor > MAX_USHORT)) {
  429. return STATUS_NOT_SUPPORTED;
  430. }
  431. //
  432. // Map the registers if needed.
  433. //
  434. if ((Device->PhysicalMemoryBase != 0) &&
  435. (Device->MemoryBase == NULL)) {
  436. Device->MemoryBase = HlMapPhysicalAddress(Device->PhysicalMemoryBase,
  437. Device->RegionSize,
  438. TRUE);
  439. if (Device->MemoryBase == NULL) {
  440. return STATUS_INSUFFICIENT_RESOURCES;
  441. }
  442. }
  443. //
  444. // Determine the correct register access function.
  445. //
  446. if (Device->MemoryBase != NULL) {
  447. Device->Read8 = HlpNs16550ReadMemory8;
  448. Device->Write8 = HlpNs16550WriteMemory8;
  449. } else {
  450. Device->Read8 = HlpNs16550ReadIo8;
  451. Device->Write8 = HlpNs16550WriteIo8;
  452. }
  453. //
  454. // Begin programming the 16550 controller. The topmost bit in the line
  455. // control register turns the DLAB (Data Latch Address Byte) on. This
  456. // changes the meanings of the registers, allowing us to program the baud
  457. // rate divisor values.
  458. //
  459. Value = NS16550_READ8(Device, Ns16550LineControl);
  460. Value |= 0x80;
  461. NS16550_WRITE8(Device, Ns16550LineControl, Value);
  462. //
  463. // Set the divisor bytes. This programs the baud rate generator.
  464. //
  465. NS16550_WRITE8(Device, Ns16550DivisorLow, (UCHAR)(Divisor & 0x00FF));
  466. NS16550_WRITE8(Device,
  467. Ns16550DivisorHigh,
  468. (UCHAR)((Divisor >> 8) & 0x00FF));
  469. //
  470. // Now program the FIFO queue configuration. It is assumed that the FIFOs
  471. // are operational, which is not true on certain machines with very old
  472. // UARTs. Setting bit 0 enables the FIFO. Setting bits 1 and 2 clears both
  473. // FIFOs. Clearing bit 3 disables DMA mode. The top 4 bits vary depending
  474. // on the version. Setting bit 5 enables the 64 byte FIFO, which is only
  475. // available on 16750s. Bit 4 is reserved. Otherwise bits 4 and 5 are
  476. // either reserved or dictate the transmit FIFO's empty trigger. Bits 6 and
  477. // 7 set the receive FIFO's trigger, where setting both bits means that
  478. // "2 less than full", which for the default 16 byte FIFO means 14 bytes
  479. // are in the buffer.
  480. //
  481. Value = 0xC7;
  482. if ((Device->Flags &
  483. DEBUG_PORT_16550_OEM_FLAG_TRANSMIT_TRIGGER_2_CHARACTERS) != 0) {
  484. Value |= 0x10;
  485. } else if ((Device->Flags & DEBUG_PORT_16550_OEM_FLAG_64_BYTE_FIFO) != 0) {
  486. Value |= 0x20;
  487. }
  488. NS16550_WRITE8(Device, Ns16550FifoControl, Value);
  489. //
  490. // Now program the Line Control register again. Setting bits 0 and 1 sets
  491. // 8 data bits. Clearing bit 2 sets one stop bit. Clearing bit 3 sets no
  492. // parity. Additionally, clearing bit 7 turns the DLAB latch off, changing
  493. // the meaning of the registers back and allowing other control registers to
  494. // be accessed.
  495. //
  496. NS16550_WRITE8(Device, Ns16550LineControl, 0x03);
  497. //
  498. // Setting the Modem Control register to zero disables all hardware flow
  499. // control.
  500. //
  501. NS16550_WRITE8(Device, Ns16550ModemControl, 0x00);
  502. return STATUS_SUCCESS;
  503. }
  504. KSTATUS
  505. HlpNs16550Transmit (
  506. PVOID Context,
  507. PVOID Data,
  508. ULONG Size
  509. )
  510. /*++
  511. Routine Description:
  512. This routine transmits data from the host out through the debug device.
  513. Arguments:
  514. Context - Supplies the pointer to the port's context, provided by the
  515. hardware module upon initialization.
  516. Data - Supplies a pointer to the data to write.
  517. Size - Supplies the size to write, in bytes.
  518. Return Value:
  519. STATUS_SUCCESS on success.
  520. STATUS_DEVICE_IO_ERROR if a device error occurred.
  521. --*/
  522. {
  523. ULONG ByteIndex;
  524. PUCHAR Bytes;
  525. PNS16550 Device;
  526. UCHAR StatusRegister;
  527. Device = Context;
  528. Bytes = Data;
  529. for (ByteIndex = 0; ByteIndex < Size; ByteIndex += 1) {
  530. //
  531. // Spin waiting for the buffer to become ready to send. If an error is
  532. // detected, bail out and report to the caller.
  533. //
  534. do {
  535. StatusRegister = NS16550_READ8(Device, Ns16550LineStatus);
  536. if ((StatusRegister & NS16550_LINE_STATUS_ERRORS) != 0) {
  537. return STATUS_DEVICE_IO_ERROR;
  538. }
  539. } while ((StatusRegister & NS16550_LINE_STATUS_TRANSMIT_EMPTY) == 0);
  540. //
  541. // Send the byte and return.
  542. //
  543. NS16550_WRITE8(Device, Ns16550Data, Bytes[ByteIndex]);
  544. }
  545. return STATUS_SUCCESS;
  546. }
  547. KSTATUS
  548. HlpNs16550Receive (
  549. PVOID Context,
  550. PVOID Data,
  551. PULONG Size
  552. )
  553. /*++
  554. Routine Description:
  555. This routine receives incoming data from the debug device. If no data is
  556. available, this routine should return immediately. If only some of the
  557. requested data is available, this routine should return the data that can
  558. be obtained now and return.
  559. Arguments:
  560. Context - Supplies the pointer to the port's context, provided by the
  561. hardware module upon initialization.
  562. Data - Supplies a pointer where the read data will be returned on success.
  563. Size - Supplies a pointer that on input contains the size of the receive
  564. buffer. On output, returns the number of bytes read.
  565. Return Value:
  566. STATUS_SUCCESS on success.
  567. STATUS_NO_DATA_AVAILABLE if there was no data to be read at the current
  568. time.
  569. STATUS_DEVICE_IO_ERROR if a device error occurred.
  570. --*/
  571. {
  572. ULONG ByteCount;
  573. ULONG ByteIndex;
  574. PUCHAR Bytes;
  575. PNS16550 Device;
  576. KSTATUS Status;
  577. UCHAR StatusRegister;
  578. Device = Context;
  579. ByteCount = *Size;
  580. Bytes = Data;
  581. Status = STATUS_NO_DATA_AVAILABLE;
  582. for (ByteIndex = 0; ByteIndex < ByteCount; ByteIndex += 1) {
  583. StatusRegister = NS16550_READ8(Device, Ns16550LineStatus);
  584. if ((StatusRegister & NS16550_LINE_STATUS_ERRORS) != 0) {
  585. Status = STATUS_DEVICE_IO_ERROR;
  586. break;
  587. }
  588. if ((StatusRegister & NS16550_LINE_STATUS_DATA_READY) == 0) {
  589. break;
  590. }
  591. Bytes[ByteIndex] = NS16550_READ8(Device, Ns16550Data);
  592. Status = STATUS_SUCCESS;
  593. }
  594. *Size = ByteIndex;
  595. return Status;
  596. }
  597. KSTATUS
  598. HlpNs16550GetStatus (
  599. PVOID Context,
  600. PBOOL ReceiveDataAvailable
  601. )
  602. /*++
  603. Routine Description:
  604. This routine returns the current device status.
  605. Arguments:
  606. Context - Supplies the pointer to the port's context, provided by the
  607. hardware module upon initialization.
  608. ReceiveDataAvailable - Supplies a pointer where a boolean will be returned
  609. indicating whether or not receive data is available.
  610. Return Value:
  611. Status code.
  612. --*/
  613. {
  614. PNS16550 Device;
  615. BYTE StatusRegister;
  616. Device = Context;
  617. *ReceiveDataAvailable = FALSE;
  618. StatusRegister = NS16550_READ8(Device, Ns16550LineStatus);
  619. if ((StatusRegister & NS16550_LINE_STATUS_DATA_READY) != 0) {
  620. *ReceiveDataAvailable = TRUE;
  621. }
  622. return STATUS_SUCCESS;
  623. }
  624. VOID
  625. HlpNs16550Disconnect (
  626. PVOID Context
  627. )
  628. /*++
  629. Routine Description:
  630. This routine disconnects a device, taking it offline.
  631. Arguments:
  632. Context - Supplies the pointer to the port's context, provided by the
  633. hardware module upon initialization.
  634. Return Value:
  635. None.
  636. --*/
  637. {
  638. return;
  639. }
  640. UCHAR
  641. HlpNs16550ReadIo8 (
  642. PNS16550 Device,
  643. NS16550_REGISTER Register
  644. )
  645. /*++
  646. Routine Description:
  647. This routine reads a 16550 register from an I/O port.
  648. Arguments:
  649. Device - Supplies a pointer to the device context.
  650. Register - Supplies the register to read.
  651. Return Value:
  652. Returns the value at the register.
  653. --*/
  654. {
  655. USHORT Port;
  656. Port = Device->IoBase + NS16550_REGISTER_OFFSET(Device, Register);
  657. return HlIoPortInByte(Port);
  658. }
  659. VOID
  660. HlpNs16550WriteIo8 (
  661. PNS16550 Device,
  662. NS16550_REGISTER Register,
  663. UCHAR Value
  664. )
  665. /*++
  666. Routine Description:
  667. This routine writes to an I/O port based 16550 register.
  668. Arguments:
  669. Device - Supplies a pointer to the device context.
  670. Register - Supplies the register to write to.
  671. Value - Supplies the value to write.
  672. Return Value:
  673. None.
  674. --*/
  675. {
  676. USHORT Port;
  677. Port = Device->IoBase + NS16550_REGISTER_OFFSET(Device, Register);
  678. HlIoPortOutByte(Port, Value);
  679. return;
  680. }
  681. UCHAR
  682. HlpNs16550ReadMemory8 (
  683. PNS16550 Device,
  684. NS16550_REGISTER Register
  685. )
  686. /*++
  687. Routine Description:
  688. This routine reads a 16550 register from a memory mapped register.
  689. Arguments:
  690. Device - Supplies a pointer to the device context.
  691. Register - Supplies the register to read.
  692. Return Value:
  693. Returns the value at the register.
  694. --*/
  695. {
  696. PVOID Address;
  697. UCHAR Value;
  698. Address = Device->MemoryBase + NS16550_REGISTER_OFFSET(Device, Register);
  699. switch (Device->RegisterShift) {
  700. case NS16550_1_BYTE_REGISTER_SHIFT:
  701. Value = HlReadRegister8(Address);
  702. break;
  703. case NS16550_2_BYTE_REGISTER_SHIFT:
  704. case NS16550_4_BYTE_REGISTER_SHIFT:
  705. default:
  706. Value = HlReadRegister32(Address);
  707. break;
  708. }
  709. return Value;
  710. }
  711. VOID
  712. HlpNs16550WriteMemory8 (
  713. PNS16550 Device,
  714. NS16550_REGISTER Register,
  715. UCHAR Value
  716. )
  717. /*++
  718. Routine Description:
  719. This routine writes to a memory mapped 16550 register.
  720. Arguments:
  721. Device - Supplies a pointer to the device context.
  722. Register - Supplies the register to write to.
  723. Value - Supplies the value to write.
  724. Return Value:
  725. None.
  726. --*/
  727. {
  728. PVOID Address;
  729. Address = Device->MemoryBase + NS16550_REGISTER_OFFSET(Device, Register);
  730. switch (Device->RegisterShift) {
  731. case NS16550_1_BYTE_REGISTER_SHIFT:
  732. HlWriteRegister8(Address, Value);
  733. break;
  734. case NS16550_2_BYTE_REGISTER_SHIFT:
  735. case NS16550_4_BYTE_REGISTER_SHIFT:
  736. default:
  737. HlWriteRegister32(Address, (ULONG)Value);
  738. break;
  739. }
  740. return;
  741. }