* Added simplified possibility to schedule UHCI transfers from within KDL.

* Added debugger commands to resolve usb_ids to pipes.
* Adjusted the physical memory allocator to be usable in a slimmed down mode
  when running inside the kernel debugger.
* Implemented USB keyboard support for KDL through a kernel debugger add-on.
* Added kgetc() and made use of it where previously individual methods were used
  to ensure that reading characters always goes through the kernel debugger
  add-ons and the other methods.

This has some preconditions to meet though:
1) The keyboard must be in the boot protocol (currently the case but needs to
   be revisited once we have a full usb_hid).
2) The keyboard must be attached to a UHCI root port (i.e. not use EHCI or OHCI,
   also not through hubs unless those are USB 1.1).
3) the usb_hid driver has to be opened for this to work. This means that for the
   time between initializing USB and when usb_hid is opened by the input_server
   there is no keyboard support.

Also note that this has no way of detecting hot-plug, meaning that you can't
re-attach your USB keyboard from the hub to the root port once in KDL.

On the bright side of things, since this is a non-destructive mechanism it is
possible to enter and leave KDL without loosing the USB state.

Tested OK in QEMU, not tested on real hardware yet, will see in a few minutes.


git-svn-id: file:///srv/svn/repos/haiku/haiku/trunk@29291 a95241bf-73f2-0310-859d-f6bbb57e9c96
This commit is contained in:
Michael Lotz
2009-02-22 20:46:27 +00:00
parent e2c33fb995
commit 6eba063647
13 changed files with 266 additions and 39 deletions
+2 -1
View File
@@ -156,7 +156,8 @@ AddFilesToHaikuImage beos system add-ons kernel busses usb
: <usb>uhci <usb>ohci <usb>ehci ; : <usb>uhci <usb>ohci <usb>ehci ;
AddFilesToHaikuImage beos system add-ons kernel console : vga_text ; AddFilesToHaikuImage beos system add-ons kernel console : vga_text ;
AddFilesToHaikuImage beos system add-ons kernel debugger AddFilesToHaikuImage beos system add-ons kernel debugger
: $(X86_ONLY)<kdebug>disasm <kdebug>hangman <kdebug>invalidate_on_exit ; : $(X86_ONLY)<kdebug>disasm <kdebug>hangman <kdebug>invalidate_on_exit
<kdebug>usb_keyboard ;
AddFilesToHaikuImage beos system add-ons kernel file_systems AddFilesToHaikuImage beos system add-ons kernel file_systems
: $(BEOS_ADD_ONS_FILE_SYSTEMS) ; : $(BEOS_ADD_ONS_FILE_SYSTEMS) ;
AddFilesToHaikuImage beos system add-ons kernel generic AddFilesToHaikuImage beos system add-ons kernel generic
+1
View File
@@ -115,6 +115,7 @@ extern void debug_set_page_fault_info(addr_t faultAddress, addr_t pc,
extern debug_page_fault_info* debug_get_page_fault_info(); extern debug_page_fault_info* debug_get_page_fault_info();
extern void debug_trap_cpu_in_kdl(bool returnIfHandedOver); extern void debug_trap_cpu_in_kdl(bool returnIfHandedOver);
extern char kgetc(void);
extern void kputs(const char *string); extern void kputs(const char *string);
extern void kputs_unfiltered(const char *string); extern void kputs_unfiltered(const char *string);
extern void kprintf_unfiltered(const char *format, ...) extern void kprintf_unfiltered(const char *format, ...)
@@ -66,6 +66,12 @@ PhysicalMemoryAllocator::PhysicalMemoryAllocator(const char *name,
fManagedMemory = fBlockSize[0] * fArrayLength[0]; fManagedMemory = fBlockSize[0] * fArrayLength[0];
size_t roundedSize = biggestSize * minCountPerBlock; size_t roundedSize = biggestSize * minCountPerBlock;
#if KDEBUG
fDebugBase = roundedSize;
fDebugChunkSize = 64;
fDebugUseMap = 0;
roundedSize += sizeof(fDebugUseMap) * 8 * fDebugChunkSize;
#endif
roundedSize = (roundedSize + B_PAGE_SIZE - 1) & ~(B_PAGE_SIZE - 1); roundedSize = (roundedSize + B_PAGE_SIZE - 1) & ~(B_PAGE_SIZE - 1);
fArea = create_area(fName, &fLogicalBase, B_ANY_KERNEL_ADDRESS, fArea = create_area(fName, &fLogicalBase, B_ANY_KERNEL_ADDRESS,
@@ -122,6 +128,24 @@ status_t
PhysicalMemoryAllocator::Allocate(size_t size, void **logicalAddress, PhysicalMemoryAllocator::Allocate(size_t size, void **logicalAddress,
void **physicalAddress) void **physicalAddress)
{ {
#if KDEBUG
if (debug_debugger_running()) {
for (int32 i = 0; i < 64; i++) {
uint64 mask = 1LL << i;
if ((fDebugUseMap & mask) == 0) {
fDebugUseMap |= mask;
*logicalAddress = (void *)((uint8 *)fLogicalBase + fDebugBase
+ i * fDebugChunkSize);
*physicalAddress = (void *)((uint8 *)fPhysicalBase + fDebugBase
+ i * fDebugChunkSize);
return B_OK;
}
}
return B_NO_MEMORY;
}
#endif
if (size == 0 || size > fBlockSize[fArrayCount - 1]) { if (size == 0 || size > fBlockSize[fArrayCount - 1]) {
TRACE_ERROR(("PMA: bad value for allocate (%ld bytes)\n", size)); TRACE_ERROR(("PMA: bad value for allocate (%ld bytes)\n", size));
return B_BAD_VALUE; return B_BAD_VALUE;
@@ -198,6 +222,15 @@ status_t
PhysicalMemoryAllocator::Deallocate(size_t size, void *logicalAddress, PhysicalMemoryAllocator::Deallocate(size_t size, void *logicalAddress,
void *physicalAddress) void *physicalAddress)
{ {
#if KDEBUG
if (debug_debugger_running()) {
uint32 index = ((uint8 *)logicalAddress - (uint8 *)fLogicalBase
- fDebugBase) / fDebugChunkSize;
fDebugUseMap &= ~(1LL << index);
return B_OK;
}
#endif
if (size == 0 || size > fBlockSize[fArrayCount - 1]) { if (size == 0 || size > fBlockSize[fArrayCount - 1]) {
TRACE_ERROR(("PMA: bad value for deallocate (%ld bytes)\n", size)); TRACE_ERROR(("PMA: bad value for deallocate (%ld bytes)\n", size));
return B_BAD_VALUE; return B_BAD_VALUE;
@@ -57,6 +57,12 @@ private:
size_t *fArrayLength; size_t *fArrayLength;
size_t *fArrayOffset; size_t *fArrayOffset;
uint8 **fArray; uint8 **fArray;
#if KDEBUG
uint32 fDebugBase;
uint32 fDebugChunkSize;
uint64 fDebugUseMap;
#endif
}; };
#endif // !_PHYSICAL_MEMORY_ALLOCATOR_H_ #endif // !_PHYSICAL_MEMORY_ALLOCATOR_H_
@@ -206,6 +206,15 @@ Stack::GetObject(usb_id id)
} }
Object *
Stack::GetObjectNoLock(usb_id id)
{
if (id >= fObjectMaxCount)
return NULL;
return fObjectArray[id];
}
int32 int32
Stack::ExploreThread(void *data) Stack::ExploreThread(void *data)
{ {
@@ -16,6 +16,25 @@
Stack *gUSBStack = NULL; Stack *gUSBStack = NULL;
static int
debug_get_pipe_for_id(int argc, char **argv)
{
if (gUSBStack == NULL)
return 1;
if (!is_debug_variable_defined("_usbPipeID"))
return 2;
uint64 id = get_debug_variable("_usbPipeID", 0);
Object *object = gUSBStack->GetObjectNoLock((usb_id)id);
if (!object || (object->Type() & USB_OBJECT_PIPE) == 0)
return 3;
set_debug_variable("_usbPipe", (uint64)object);
return 0;
}
static int32 static int32
bus_std_ops(int32 op, ...) bus_std_ops(int32 op, ...)
{ {
@@ -58,6 +77,9 @@ bus_std_ops(int32 op, ...)
} }
gUSBStack = stack; gUSBStack = stack;
add_debugger_command("get_usb_pipe_for_id",
&debug_get_pipe_for_id,
"Gets the config for a USB pipe");
#ifndef __HAIKU__ #ifndef __HAIKU__
// Plain R5 workaround, see comment above. // Plain R5 workaround, see comment above.
shared = create_area("shared usb stack", &address, shared = create_area("shared usb stack", &address,
@@ -73,6 +95,8 @@ bus_std_ops(int32 op, ...)
TRACE_MODULE("uninit\n"); TRACE_MODULE("uninit\n");
delete gUSBStack; delete gUSBStack;
gUSBStack = NULL; gUSBStack = NULL;
remove_debugger_command("get_usb_pipe_for_id",
&debug_get_pipe_for_id);
break; break;
default: default:
@@ -126,6 +126,9 @@ public:
void PutUSBID(usb_id id); void PutUSBID(usb_id id);
Object * GetObject(usb_id id); Object * GetObject(usb_id id);
// only for the kernel debugger
Object * GetObjectNoLock(usb_id id);
void AddBusManager(BusManager *bus); void AddBusManager(BusManager *bus);
int32 IndexOfBusManager(BusManager *bus); int32 IndexOfBusManager(BusManager *bus);
BusManager * BusManagerAt(int32 index); BusManager * BusManagerAt(int32 index);
+123 -9
View File
@@ -19,6 +19,33 @@
pci_module_info *UHCI::sPCIModule = NULL; pci_module_info *UHCI::sPCIModule = NULL;
static int32 sDebuggerCommandAdded = 0;
static int
debug_process_transfer(int argc, char **argv)
{
Pipe *pipe = (Pipe *)get_debug_variable("_usbPipe", 0);
if (pipe == NULL)
return 2;
// check if we have a UHCI bus at all
if (pipe->GetBusManager()->TypeName()[0] != 'u')
return 3;
uint8 *data = (uint8 *)get_debug_variable("_usbTransferData", 0);
if (data == NULL)
return 4;
size_t length = (size_t)get_debug_variable("_usbTransferLength", 0);
if (length == 0)
return 5;
Transfer transfer(pipe);
transfer.SetData(data, length);
return ((UHCI *)pipe->GetBusManager())->ProcessDebugTransfer(&transfer);
}
static int32 static int32
uhci_std_ops(int32 op, ...) uhci_std_ops(int32 op, ...)
@@ -197,9 +224,9 @@ Queue::TerminateByStrayDescriptor()
status_t status_t
Queue::AppendTransfer(uhci_qh *transfer) Queue::AppendTransfer(uhci_qh *transfer, bool lock)
{ {
if (!Lock()) if (lock && !Lock())
return B_ERROR; return B_ERROR;
transfer->link_log = NULL; transfer->link_log = NULL;
@@ -219,15 +246,16 @@ Queue::AppendTransfer(uhci_qh *transfer)
element->link_phy = transfer->this_phy | QH_NEXT_IS_QH; element->link_phy = transfer->this_phy | QH_NEXT_IS_QH;
} }
Unlock(); if (lock)
Unlock();
return B_OK; return B_OK;
} }
status_t status_t
Queue::RemoveTransfer(uhci_qh *transfer) Queue::RemoveTransfer(uhci_qh *transfer, bool lock)
{ {
if (!Lock()) if (lock && !Lock())
return B_ERROR; return B_ERROR;
if (fQueueTop == transfer) { if (fQueueTop == transfer) {
@@ -241,7 +269,8 @@ Queue::RemoveTransfer(uhci_qh *transfer)
fQueueHead->element_phy = transfer->link_phy; fQueueHead->element_phy = transfer->link_phy;
} }
Unlock(); if (lock)
Unlock();
return B_OK; return B_OK;
} else { } else {
uhci_qh *element = fQueueTop; uhci_qh *element = fQueueTop;
@@ -249,7 +278,8 @@ Queue::RemoveTransfer(uhci_qh *transfer)
if (element->link_log == transfer) { if (element->link_log == transfer) {
element->link_log = transfer->link_log; element->link_log = transfer->link_log;
element->link_phy = transfer->link_phy; element->link_phy = transfer->link_phy;
Unlock(); if (lock)
Unlock();
return B_OK; return B_OK;
} }
@@ -257,7 +287,8 @@ Queue::RemoveTransfer(uhci_qh *transfer)
} }
} }
Unlock(); if (lock)
Unlock();
return B_BAD_VALUE; return B_BAD_VALUE;
} }
@@ -374,8 +405,9 @@ UHCI::UHCI(pci_info *info, Stack *stack)
// 1: low speed control transfers // 1: low speed control transfers
// 2: full speed control transfers // 2: full speed control transfers
// 3: bulk transfers // 3: bulk transfers
// 4: debug queue
// TODO: 4: bandwidth reclamation queue // TODO: 4: bandwidth reclamation queue
fQueueCount = 4; fQueueCount = 5;
fQueues = new(std::nothrow) Queue *[fQueueCount]; fQueues = new(std::nothrow) Queue *[fQueueCount];
if (!fQueues) { if (!fQueues) {
delete_area(fFrameArea); delete_area(fFrameArea);
@@ -462,6 +494,12 @@ UHCI::UHCI(pci_info *info, Stack *stack)
WriteReg16(UHCI_USBINTR, UHCI_USBINTR_CRC | UHCI_USBINTR_IOC WriteReg16(UHCI_USBINTR, UHCI_USBINTR_CRC | UHCI_USBINTR_IOC
| UHCI_USBINTR_SHORT); | UHCI_USBINTR_SHORT);
if (atomic_add(&sDebuggerCommandAdded, 1) == 0) {
add_debugger_command("uhci_process_transfer",
&debug_process_transfer,
"Processes a USB transfer with the given variables");
}
TRACE("UHCI host controller driver constructed\n"); TRACE("UHCI host controller driver constructed\n");
fInitOK = true; fInitOK = true;
} }
@@ -469,6 +507,11 @@ UHCI::UHCI(pci_info *info, Stack *stack)
UHCI::~UHCI() UHCI::~UHCI()
{ {
if (atomic_add(&sDebuggerCommandAdded, -1) == 1) {
remove_debugger_command("uhci_process_transfer",
&debug_process_transfer);
}
int32 result = 0; int32 result = 0;
fStopFinishThread = true; fStopFinishThread = true;
fStopFinishIsochronousThread = true; fStopFinishIsochronousThread = true;
@@ -607,6 +650,77 @@ UHCI::SubmitTransfer(Transfer *transfer)
} }
status_t
UHCI::ProcessDebugTransfer(Transfer *transfer)
{
uhci_td *firstDescriptor = NULL;
uhci_qh *transferQueue = NULL;
status_t result = CreateFilledTransfer(transfer, &firstDescriptor,
&transferQueue);
if (result < B_OK)
return result;
fQueues[UHCI_DEBUG_QUEUE]->AppendTransfer(transferQueue, false);
while (true) {
bool transferOK = false;
bool transferError = false;
uhci_td *descriptor = firstDescriptor;
while (descriptor) {
uint32 status = descriptor->status;
if (status & TD_STATUS_ACTIVE)
break;
if (status & TD_ERROR_MASK) {
transferError = true;
break;
}
if ((descriptor->link_phy & TD_TERMINATE)
|| (descriptor->status & TD_STATUS_ACTLEN_MASK)
< (descriptor->token >> TD_TOKEN_MAXLEN_SHIFT)) {
transferOK = true;
break;
}
descriptor = (uhci_td *)descriptor->link_log;
}
if (!transferOK && !transferError) {
spin(200);
continue;
}
if (transferOK) {
size_t actualLength = 0;
uint8 lastDataToggle = 0;
if (transfer->TransferPipe()->Direction() == Pipe::In) {
// data to read out
iovec *vector = transfer->Vector();
size_t vectorCount = transfer->VectorCount();
actualLength = ReadDescriptorChain(firstDescriptor,
vector, vectorCount, &lastDataToggle);
} else {
// read the actual length that was sent
actualLength = ReadActualLength(firstDescriptor,
&lastDataToggle);
}
transfer->TransferPipe()->SetDataToggle(lastDataToggle == 0);
}
fQueues[UHCI_DEBUG_QUEUE]->RemoveTransfer(transferQueue, false);
FreeDescriptorChain(firstDescriptor);
FreeTransferQueue(transferQueue);
return transferOK ? B_OK : B_IO_ERROR;
}
return B_ERROR;
}
status_t status_t
UHCI::CancelQueuedTransfers(Pipe *pipe, bool force) UHCI::CancelQueuedTransfers(Pipe *pipe, bool force)
{ {
+6 -2
View File
@@ -20,6 +20,7 @@
#define UHCI_FULL_SPEED_CONTROL_QUEUE 2 #define UHCI_FULL_SPEED_CONTROL_QUEUE 2
#define UHCI_BULK_QUEUE 3 #define UHCI_BULK_QUEUE 3
#define UHCI_BANDWIDTH_RECLAMATION_QUEUE 4 #define UHCI_BANDWIDTH_RECLAMATION_QUEUE 4
#define UHCI_DEBUG_QUEUE 4
struct pci_info; struct pci_info;
struct pci_module_info; struct pci_module_info;
@@ -39,8 +40,10 @@ public:
status_t LinkTo(Queue *other); status_t LinkTo(Queue *other);
status_t TerminateByStrayDescriptor(); status_t TerminateByStrayDescriptor();
status_t AppendTransfer(uhci_qh *transfer); status_t AppendTransfer(uhci_qh *transfer,
status_t RemoveTransfer(uhci_qh *transfer); bool lock = true);
status_t RemoveTransfer(uhci_qh *transfer,
bool lock = true);
addr_t PhysicalAddress(); addr_t PhysicalAddress();
@@ -94,6 +97,7 @@ public:
status_t Start(); status_t Start();
virtual status_t SubmitTransfer(Transfer *transfer); virtual status_t SubmitTransfer(Transfer *transfer);
status_t ProcessDebugTransfer(Transfer *transfer);
virtual status_t CancelQueuedTransfers(Pipe *pipe, bool force); virtual status_t CancelQueuedTransfers(Pipe *pipe, bool force);
status_t CancelQueuedIsochronousTransfers(Pipe *pipe, bool force); status_t CancelQueuedIsochronousTransfers(Pipe *pipe, bool force);
status_t SubmitRequest(Transfer *transfer); status_t SubmitRequest(Transfer *transfer);
+1
View File
@@ -7,3 +7,4 @@ SubInclude HAIKU_TOP src add-ons kernel debugger disasm ;
SubInclude HAIKU_TOP src add-ons kernel debugger hangman ; SubInclude HAIKU_TOP src add-ons kernel debugger hangman ;
SubInclude HAIKU_TOP src add-ons kernel debugger invalidate_on_exit ; SubInclude HAIKU_TOP src add-ons kernel debugger invalidate_on_exit ;
SubInclude HAIKU_TOP src add-ons kernel debugger laplinkll ; SubInclude HAIKU_TOP src add-ons kernel debugger laplinkll ;
SubInclude HAIKU_TOP src add-ons kernel debugger usb_keyboard ;
@@ -10,11 +10,26 @@
#include "KeyboardDevice.h" #include "KeyboardDevice.h"
#include <string.h> #include <string.h>
#include <usb/USB_hid.h> #include <usb/USB_hid.h>
#include <debug.h>
// input server private for raw_key_info, KB_READ, etc... // input server private for raw_key_info, KB_READ, etc...
#include "kb_mouse_driver.h" #include "kb_mouse_driver.h"
static usb_id sDebugKeyboardPipe = 0;
static size_t sDebugKeyboardReportSize = 0;
static int32 sDebuggerCommandAdded = 0;
static int
debug_get_keyboard_config(int argc, char **argv)
{
set_debug_variable("_usbPipeID", (uint64)sDebugKeyboardPipe);
set_debug_variable("_usbReportSize", (uint64)sDebugKeyboardReportSize);
return 0;
}
KeyboardDevice::KeyboardDevice(usb_device device, usb_pipe interruptPipe, KeyboardDevice::KeyboardDevice(usb_device device, usb_pipe interruptPipe,
size_t interfaceIndex, report_insn *instructions, size_t instructionCount, size_t interfaceIndex, report_insn *instructions, size_t instructionCount,
size_t totalReportSize) size_t totalReportSize)
@@ -34,12 +49,23 @@ KeyboardDevice::KeyboardDevice(usb_device device, usb_pipe interruptPipe,
} }
SetBaseName("input/keyboard/usb/"); SetBaseName("input/keyboard/usb/");
if (atomic_add(&sDebuggerCommandAdded, 1) == 0) {
add_debugger_command("get_usb_keyboard_config",
&debug_get_keyboard_config,
"Gets the required config of the USB keyboard");
}
} }
KeyboardDevice::~KeyboardDevice() KeyboardDevice::~KeyboardDevice()
{ {
free(fLastTransferBuffer); free(fLastTransferBuffer);
if (atomic_add(&sDebuggerCommandAdded, -1) == 1) {
remove_debugger_command("get_usb_keyboard_config",
&debug_get_keyboard_config);
}
} }
@@ -410,9 +436,13 @@ KeyboardDevice::_InterpretBuffer()
key = KEY_Break; key = KEY_Break;
else if (key == 0xe && (current[0] & 1)) else if (key == 0xe && (current[0] & 1))
key = KEY_SysRq; key = KEY_SysRq;
#if 0 #if 1
else if (keyDown && key == 0x0d) // ToDo: remove again else if (keyDown && key == 0x0d) {
// ToDo: remove again
sDebugKeyboardPipe = fInterruptPipe;
sDebugKeyboardReportSize = fTotalReportSize;
panic("keyboard requested halt.\n"); panic("keyboard requested halt.\n");
}
#endif #endif
else if (key == 0) { else if (key == 0) {
// unmapped key // unmapped key
+2 -1
View File
@@ -9,6 +9,7 @@
#include <KernelExport.h> #include <KernelExport.h>
#include <frame_buffer_console.h> #include <frame_buffer_console.h>
#include <console.h> #include <console.h>
#include <debug.h>
#include <arch/debug_console.h> #include <arch/debug_console.h>
#include <string.h> #include <string.h>
@@ -139,7 +140,7 @@ next_line(void)
text[i], sScreen.boot_debug_output ? 0x6f : 0xf6); text[i], sScreen.boot_debug_output ? 0x6f : 0xf6);
} }
char c = blue_screen_getchar(); char c = kgetc();
if (c == 's') { if (c == 's') {
sScreen.ignore_output = true; sScreen.ignore_output = true;
} else if (c == 'q' && in_command_invocation()) { } else if (c == 'q' && in_command_invocation()) {
+24 -24
View File
@@ -408,27 +408,8 @@ read_line(char* buffer, int32 maxLength,
bool done = false; bool done = false;
char c = 0; char c = 0;
char (*readChar)(void);
if (sBlueScreenOutput)
readChar = blue_screen_getchar;
else
readChar = arch_debug_serial_getchar;
while (!done) { while (!done) {
bool hasChar = false; c = kgetc();
for (uint32 i = 0; i < kMaxDebuggerModules; i++) {
if (sDebuggerModules[i] && sDebuggerModules[i]->debugger_getchar) {
int getChar = sDebuggerModules[i]->debugger_getchar();
if (getChar >= 0) {
hasChar = true;
c = (char)getChar;
break;
}
}
}
if (!hasChar)
c = readChar();
switch (c) { switch (c) {
case '\n': case '\n':
@@ -487,12 +468,12 @@ read_line(char* buffer, int32 maxLength,
} }
break; break;
case 27: // escape sequence case 27: // escape sequence
c = readChar(); c = kgetc();
if (c != '[') { if (c != '[') {
// ignore broken escape sequence // ignore broken escape sequence
break; break;
} }
c = readChar(); c = kgetc();
switch (c) { switch (c) {
case 'C': // right arrow case 'C': // right arrow
if (position < length) { if (position < length) {
@@ -548,7 +529,7 @@ read_line(char* buffer, int32 maxLength,
case '5': // if "5~", it's PAGE UP case '5': // if "5~", it's PAGE UP
case '6': // if "6~", it's PAGE DOWN case '6': // if "6~", it's PAGE DOWN
{ {
if (readChar() != '~') if (kgetc() != '~')
break; break;
// PAGE UP: search backward, PAGE DOWN: forward // PAGE UP: search backward, PAGE DOWN: forward
@@ -603,7 +584,7 @@ read_line(char* buffer, int32 maxLength,
} }
case '3': // if "3~", it's DEL case '3': // if "3~", it's DEL
{ {
if (readChar() != '~') if (kgetc() != '~')
break; break;
if (position < length) if (position < length)
@@ -649,6 +630,25 @@ read_line(char* buffer, int32 maxLength,
} }
char
kgetc(void)
{
// give the kernel debugger modules a chance first
for (uint32 i = 0; i < kMaxDebuggerModules; i++) {
if (sDebuggerModules[i] && sDebuggerModules[i]->debugger_getchar) {
int getChar = sDebuggerModules[i]->debugger_getchar();
if (getChar >= 0)
return (char)getChar;
}
}
if (sBlueScreenOutput)
return blue_screen_getchar();
return arch_debug_serial_getchar();
}
int int
kgets(char* buffer, int length) kgets(char* buffer, int length)
{ {