From 159aa93b31cb5d926ef4939a16419e7d02979b1c Mon Sep 17 00:00:00 2001 From: Michael Lotz Date: Sun, 31 Aug 2014 22:45:43 +0200 Subject: [PATCH] USB: Generalize logic for debug transfers used within KDL. Pull debug transfer methods into the base BusManager class and implement them for UHCI and EHCI. This makes it possible to have a single debugger command installed by the USB stack instead of HCI specific ones. --- .../kernel/bus_managers/usb/BusManager.cpp | 23 +++ src/add-ons/kernel/bus_managers/usb/usb.cpp | 71 ++++++++- .../kernel/bus_managers/usb/usb_private.h | 8 + src/add-ons/kernel/busses/usb/ehci.cpp | 148 ++++-------------- src/add-ons/kernel/busses/usb/ehci.h | 12 +- src/add-ons/kernel/busses/usb/uhci.cpp | 144 ++++------------- src/add-ons/kernel/busses/usb/uhci.h | 12 +- 7 files changed, 171 insertions(+), 247 deletions(-) diff --git a/src/add-ons/kernel/bus_managers/usb/BusManager.cpp b/src/add-ons/kernel/bus_managers/usb/BusManager.cpp index 4889375989..99f7953866 100644 --- a/src/add-ons/kernel/bus_managers/usb/BusManager.cpp +++ b/src/add-ons/kernel/bus_managers/usb/BusManager.cpp @@ -264,6 +264,29 @@ BusManager::Stop() } +status_t +BusManager::StartDebugTransfer(Transfer *transfer) +{ + // virtual function to be overridden + return B_UNSUPPORTED; +} + + +status_t +BusManager::CheckDebugTransfer(Transfer *transfer) +{ + // virtual function to be overridden + return B_UNSUPPORTED; +} + + +void +BusManager::CancelDebugTransfer(Transfer *transfer) +{ + // virtual function to be overridden +} + + status_t BusManager::SubmitTransfer(Transfer *transfer) { diff --git a/src/add-ons/kernel/bus_managers/usb/usb.cpp b/src/add-ons/kernel/bus_managers/usb/usb.cpp index 718b71e73e..4b9da16afd 100644 --- a/src/add-ons/kernel/bus_managers/usb/usb.cpp +++ b/src/add-ons/kernel/bus_managers/usb/usb.cpp @@ -17,6 +17,54 @@ Stack *gUSBStack = NULL; #ifdef HAIKU_TARGET_PLATFORM_HAIKU +/*! The function is an evil hack to allow usb_keyboard to + execute transfers. + When invoked the first time, a new transfer is started, each time the + function is called afterwards, it is checked whether the transfer is already + completed. If called with argv[1] == "cancel" the function cancels a + possibly pending transfer. +*/ +static status_t +debug_run_transfer(Pipe *pipe, uint8 *data, size_t dataLength, + usb_request_data *requestData, bool cancel) +{ + static uint8 transferBuffer[sizeof(Transfer)] + __attribute__((aligned(16))); + static Transfer *transfer = NULL; + + BusManager *bus = pipe->GetBusManager(); + + if (cancel) { + if (transfer != NULL) { + bus->CancelDebugTransfer(transfer); + transfer = NULL; + } + + return B_OK; + } + + if (transfer != NULL) { + status_t error = bus->CheckDebugTransfer(transfer); + if (error != B_DEV_PENDING) + transfer = NULL; + + return error; + } + + transfer = new(transferBuffer) Transfer(pipe); + transfer->SetData(data, dataLength); + transfer->SetRequestData(requestData); + + status_t error = bus->StartDebugTransfer(transfer); + if (error != B_OK) { + transfer = NULL; + return error; + } + + return B_DEV_PENDING; +} + + static int debug_get_pipe_for_id(int argc, char **argv) { @@ -34,6 +82,23 @@ debug_get_pipe_for_id(int argc, char **argv) set_debug_variable("_usbPipe", (uint64)object); return 0; } + + +static int +debug_process_transfer(int argc, char **argv) +{ + Pipe *pipe = (Pipe *)get_debug_variable("_usbPipe", 0); + if (pipe == NULL) + return B_BAD_VALUE; + + uint8 *data = (uint8 *)get_debug_variable("_usbTransferData", 0); + size_t length = (size_t)get_debug_variable("_usbTransferLength", 0); + usb_request_data *requestData + = (usb_request_data *)get_debug_variable("_usbRequestData", 0); + + return debug_run_transfer(pipe, data, length, requestData, + argc > 1 && strcmp(argv[1], "cancel") == 0); +} #endif @@ -83,7 +148,11 @@ bus_std_ops(int32 op, ...) #ifdef HAIKU_TARGET_PLATFORM_HAIKU add_debugger_command("get_usb_pipe_for_id", &debug_get_pipe_for_id, - "Gets the config for a USB pipe"); + "Sets _usbPipe by resolving _usbPipeID"); + add_debugger_command("usb_process_transfer", + &debug_process_transfer, + "Transfers _usbTransferData with _usbTransferLength" + " (and/or _usbRequestData) to pipe _usbPipe"); #elif HAIKU_TARGET_PLATFORM_BEOS // Plain R5 workaround, see comment above. shared = create_area("shared usb stack", &address, diff --git a/src/add-ons/kernel/bus_managers/usb/usb_private.h b/src/add-ons/kernel/bus_managers/usb/usb_private.h index 8ca6b84fe3..2e20979e88 100644 --- a/src/add-ons/kernel/bus_managers/usb/usb_private.h +++ b/src/add-ons/kernel/bus_managers/usb/usb_private.h @@ -211,6 +211,10 @@ virtual void FreeDevice(Device *device); virtual status_t Start(); virtual status_t Stop(); +virtual status_t StartDebugTransfer(Transfer *transfer); +virtual status_t CheckDebugTransfer(Transfer *transfer); +virtual void CancelDebugTransfer(Transfer *transfer); + virtual status_t SubmitTransfer(Transfer *transfer); virtual status_t CancelQueuedTransfers(Pipe *pipe, bool force); @@ -680,6 +684,10 @@ public: void SetCallback(usb_callback_func callback, void *cookie); + usb_callback_func Callback() const + { return fCallback; } + void * CallbackCookie() const + { return fCallbackCookie; } void Finished(uint32 status, size_t actualLength); diff --git a/src/add-ons/kernel/busses/usb/ehci.cpp b/src/add-ons/kernel/busses/usb/ehci.cpp index 29cb8d1e0e..dfa51f8448 100644 --- a/src/add-ons/kernel/busses/usb/ehci.cpp +++ b/src/add-ons/kernel/busses/usb/ehci.cpp @@ -22,8 +22,6 @@ pci_module_info *EHCI::sPCIModule = NULL; pci_x86_module_info *EHCI::sPCIx86Module = NULL; -static int32 sDebuggerCommandAdded = 0; - static int32 ehci_std_ops(int32 op, ...) @@ -107,83 +105,6 @@ print_queue(ehci_qh *queueHead) #endif // TRACE_USB -class DebugTransfer : public Transfer { -public: - DebugTransfer(Pipe *pipe) - : - Transfer(pipe) - { - } - - ehci_qh * queue_head; - ehci_qtd * data_descriptor; - bool direction_in; -}; - - -/*! The function is an evil hack to allow usb_keyboard to - execute transfers. - When invoked the first time, a new transfer is started, each time the - function is called afterwards, it is checked whether the transfer is already - completed. If called with argv[1] == "cancel" the function cancels a - possibly pending transfer. -*/ -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 EHCI bus at all - if (pipe->GetBusManager()->TypeName()[0] != 'e') - 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; - - static uint8 transferBuffer[sizeof(DebugTransfer)] - __attribute__((aligned(16))); - static DebugTransfer *transfer = NULL; - - EHCI *bus = (EHCI *)pipe->GetBusManager(); - - if (argc > 1 && strcmp(argv[1], "cancel") == 0) { - if (transfer != NULL) { - bus->CancelDebugTransfer(transfer); - transfer = NULL; - } - - return 0; - } - - if (transfer != NULL) { - bool stillPending; - status_t error = bus->CheckDebugTransfer(transfer, stillPending); - if (stillPending) - return 6; - - transfer = NULL; - return error == B_OK ? 0 : 7; - } - - transfer = new(transferBuffer) DebugTransfer(pipe); - transfer->SetData(data, length); - - if (bus->StartDebugTransfer(transfer) != B_OK) { - transfer = NULL; - return 7; - } - - return 8; -} - - // // #pragma mark - // @@ -636,12 +557,6 @@ EHCI::EHCI(pci_info *info, Stack *stack) TRACE("set the async list addr to 0x%08" B_PRIx32 "\n", ReadOpReg(EHCI_ASYNCLISTADDR)); - if (atomic_add(&sDebuggerCommandAdded, 1) == 0) { - add_debugger_command("ehci_process_transfer", - &debug_process_transfer, - "Processes a USB transfer with the given variables"); - } - fInitOK = true; TRACE("EHCI host controller driver constructed\n"); } @@ -651,11 +566,6 @@ EHCI::~EHCI() { TRACE("tear down EHCI host controller driver\n"); - if (atomic_add(&sDebuggerCommandAdded, -1) == 1) { - remove_debugger_command("ehci_process_transfer", - &debug_process_transfer); - } - WriteOpReg(EHCI_USBCMD, 0); WriteOpReg(EHCI_CONFIGFLAG, 0); CancelAllPendingTransfers(); @@ -783,37 +693,41 @@ EHCI::Start() status_t -EHCI::StartDebugTransfer(DebugTransfer *transfer) +EHCI::StartDebugTransfer(Transfer *transfer) { - Pipe *pipe = transfer->TransferPipe(); - transfer->queue_head = CreateQueueHead(); - if (transfer->queue_head == NULL) + static transfer_data transferData; + + transferData.queue_head = CreateQueueHead(); + if (transferData.queue_head == NULL) return B_NO_MEMORY; - status_t result = InitQueueHead(transfer->queue_head, pipe); + Pipe *pipe = transfer->TransferPipe(); + status_t result = InitQueueHead(transferData.queue_head, pipe); if (result != B_OK) { - FreeQueueHead(transfer->queue_head); + FreeQueueHead(transferData.queue_head); return result; } if ((pipe->Type() & USB_OBJECT_CONTROL_PIPE) != 0) { - result = FillQueueWithRequest(transfer, transfer->queue_head, - &transfer->data_descriptor, &transfer->direction_in); + result = FillQueueWithRequest(transfer, transferData.queue_head, + &transferData.data_descriptor, &transferData.incoming); } else { - result = FillQueueWithData(transfer, transfer->queue_head, - &transfer->data_descriptor, &transfer->direction_in); + result = FillQueueWithData(transfer, transferData.queue_head, + &transferData.data_descriptor, &transferData.incoming); } if (result != B_OK) { - FreeQueueHead(transfer->queue_head); + FreeQueueHead(transferData.queue_head); return result; } if ((pipe->Type() & USB_OBJECT_INTERRUPT_PIPE) != 0) - LinkPeriodicDebugQueueHead(transfer->queue_head, pipe); + LinkPeriodicDebugQueueHead(transferData.queue_head, pipe); else - LinkAsyncDebugQueueHead(transfer->queue_head); + LinkAsyncDebugQueueHead(transferData.queue_head); + // we abuse the callback cookie to hold our transfer data + transfer->SetCallback(NULL, &transferData); return B_OK; } @@ -853,11 +767,12 @@ EHCI::LinkPeriodicDebugQueueHead(ehci_qh *queueHead, Pipe *pipe) status_t -EHCI::CheckDebugTransfer(DebugTransfer *transfer, bool &_stillPending) +EHCI::CheckDebugTransfer(Transfer *transfer) { bool transferOK = false; bool transferError = false; - ehci_qtd *descriptor = transfer->queue_head->element_log; + transfer_data *transferData = (transfer_data *)transfer->CallbackCookie(); + ehci_qtd *descriptor = transferData->queue_head->element_log; while (descriptor) { uint32 status = descriptor->token; @@ -895,36 +810,36 @@ EHCI::CheckDebugTransfer(DebugTransfer *transfer, bool &_stillPending) if (!transferOK && !transferError) { spin(75); - _stillPending = true; - return B_OK; + return B_DEV_PENDING; } if (transferOK) { bool nextDataToggle = false; - if (transfer->data_descriptor != NULL && transfer->direction_in) { + if (transferData->data_descriptor != NULL && transferData->incoming) { // data to read out iovec *vector = transfer->Vector(); size_t vectorCount = transfer->VectorCount(); - ReadDescriptorChain(transfer->data_descriptor, + ReadDescriptorChain(transferData->data_descriptor, vector, vectorCount, &nextDataToggle); - } else if (transfer->data_descriptor != NULL) - ReadActualLength(transfer->data_descriptor, &nextDataToggle); + } else if (transferData->data_descriptor != NULL) + ReadActualLength(transferData->data_descriptor, &nextDataToggle); transfer->TransferPipe()->SetDataToggle(nextDataToggle); } CleanupDebugTransfer(transfer); - _stillPending = false; return transferOK ? B_OK : B_IO_ERROR; } void -EHCI::CancelDebugTransfer(DebugTransfer *transfer) +EHCI::CancelDebugTransfer(Transfer *transfer) { + transfer_data *transferData = (transfer_data *)transfer->CallbackCookie(); + // clear the active bit so the descriptors are canceled - ehci_qtd *descriptor = transfer->queue_head->element_log; + ehci_qtd *descriptor = transferData->queue_head->element_log; while (descriptor != NULL) { descriptor->token &= ~EHCI_QTD_STATUS_ACTIVE; descriptor = descriptor->next_log; @@ -936,9 +851,10 @@ EHCI::CancelDebugTransfer(DebugTransfer *transfer) void -EHCI::CleanupDebugTransfer(DebugTransfer *transfer) +EHCI::CleanupDebugTransfer(Transfer *transfer) { - ehci_qh *queueHead = transfer->queue_head; + transfer_data *transferData = (transfer_data *)transfer->CallbackCookie(); + ehci_qh *queueHead = transferData->queue_head; ehci_qh *prevHead = queueHead->prev_log; if (prevHead != NULL) { prevHead->next_phy = queueHead->next_phy; diff --git a/src/add-ons/kernel/busses/usb/ehci.h b/src/add-ons/kernel/busses/usb/ehci.h index 9c22ce6855..0e7aa7a80f 100644 --- a/src/add-ons/kernel/busses/usb/ehci.h +++ b/src/add-ons/kernel/busses/usb/ehci.h @@ -16,7 +16,6 @@ struct pci_info; struct pci_module_info; struct pci_x86_module_info; -class DebugTransfer; class EHCIRootHub; @@ -57,16 +56,13 @@ public: status_t Start(); - status_t StartDebugTransfer(DebugTransfer *transfer); - status_t CheckDebugTransfer(DebugTransfer *transfer, - bool &_stillPending); +virtual status_t StartDebugTransfer(Transfer *transfer); +virtual status_t CheckDebugTransfer(Transfer *transfer); void LinkAsyncDebugQueueHead(ehci_qh *queueHead); void LinkPeriodicDebugQueueHead( ehci_qh *queueHead, Pipe *pipe); - void CancelDebugTransfer( - DebugTransfer *transfer); - void CleanupDebugTransfer( - DebugTransfer *transfer); +virtual void CancelDebugTransfer(Transfer *transfer); + void CleanupDebugTransfer(Transfer *transfer); virtual status_t SubmitTransfer(Transfer *transfer); status_t SubmitIsochronous(Transfer *transfer); diff --git a/src/add-ons/kernel/busses/usb/uhci.cpp b/src/add-ons/kernel/busses/usb/uhci.cpp index e868d73856..9c93c0e825 100644 --- a/src/add-ons/kernel/busses/usb/uhci.cpp +++ b/src/add-ons/kernel/busses/usb/uhci.cpp @@ -21,86 +21,6 @@ pci_module_info *UHCI::sPCIModule = NULL; pci_x86_module_info *UHCI::sPCIx86Module = NULL; -static int32 sDebuggerCommandAdded = 0; - - -#ifdef HAIKU_TARGET_PLATFORM_HAIKU - - -class DebugTransfer : public Transfer { -public: - DebugTransfer(Pipe *pipe) - : - Transfer(pipe) - { - } - - uhci_td *firstDescriptor; - uhci_qh *transferQueue; -}; - - -/*! The function is an evil hack to allow usb_keyboard to - execute transfers. - When invoked the first time, a new transfer is started, each time the - function is called afterwards, it is checked whether the transfer is already - completed. If called with argv[1] == "cancel" the function cancels a - possibly pending transfer. -*/ -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; - - static uint8 transferBuffer[sizeof(DebugTransfer)] - __attribute__((aligned(16))); - static DebugTransfer* transfer; - - UHCI *bus = (UHCI *)pipe->GetBusManager(); - - if (argc > 1 && strcmp(argv[1], "cancel") == 0) { - if (transfer != NULL) { - bus->CancelDebugTransfer(transfer); - transfer = NULL; - } - return 0; - } - - if (transfer != NULL) { - bool stillPending; - status_t error = bus->CheckDebugTransfer(transfer, stillPending); - if (!stillPending) - transfer = NULL; - - return error == B_OK ? 0 : 6; - } - - transfer = new(transferBuffer) DebugTransfer(pipe); - transfer->SetData(data, length); - - if (bus->StartDebugTransfer(transfer) != B_OK) { - transfer = NULL; - return 7; - } - - return 0; -} -#endif - static int32 uhci_std_ops(int32 op, ...) @@ -586,14 +506,6 @@ UHCI::UHCI(pci_info *info, Stack *stack) WriteReg16(UHCI_USBINTR, UHCI_USBINTR_CRC | UHCI_USBINTR_IOC | UHCI_USBINTR_SHORT); -#ifdef HAIKU_TARGET_PLATFORM_HAIKU - if (atomic_add(&sDebuggerCommandAdded, 1) == 0) { - add_debugger_command("uhci_process_transfer", - &debug_process_transfer, - "Processes a USB transfer with the given variables"); - } -#endif - TRACE("UHCI host controller driver constructed\n"); fInitOK = true; } @@ -601,13 +513,6 @@ UHCI::UHCI(pci_info *info, Stack *stack) UHCI::~UHCI() { -#ifdef HAIKU_TARGET_PLATFORM_HAIKU - if (atomic_add(&sDebuggerCommandAdded, -1) == 1) { - remove_debugger_command("uhci_process_transfer", - &debug_process_transfer); - } -#endif - int32 result = 0; fStopThreads = true; delete_sem(fFinishTransfersSem); @@ -760,27 +665,32 @@ UHCI::SubmitTransfer(Transfer *transfer) status_t -UHCI::StartDebugTransfer(DebugTransfer *transfer) +UHCI::StartDebugTransfer(Transfer *transfer) { - transfer->firstDescriptor = NULL; - transfer->transferQueue = NULL; - status_t result = CreateFilledTransfer(transfer, &transfer->firstDescriptor, - &transfer->transferQueue); + static transfer_data transferData; + transferData.first_descriptor = NULL; + transferData.transfer_queue = NULL; + status_t result = CreateFilledTransfer(transfer, + &transferData.first_descriptor, &transferData.transfer_queue); if (result < B_OK) return result; - fQueues[UHCI_DEBUG_QUEUE]->AppendTransfer(transfer->transferQueue, false); + fQueues[UHCI_DEBUG_QUEUE]->AppendTransfer(transferData.transfer_queue, + false); + // we abuse the callback cookie to hold our transfer data + transfer->SetCallback(NULL, &transferData); return B_OK; } status_t -UHCI::CheckDebugTransfer(DebugTransfer *transfer, bool &_stillPending) +UHCI::CheckDebugTransfer(Transfer *transfer) { bool transferOK = false; bool transferError = false; - uhci_td *descriptor = transfer->firstDescriptor; + transfer_data *transferData = (transfer_data *)transfer->CallbackCookie(); + uhci_td *descriptor = transferData->first_descriptor; while (descriptor) { uint32 status = descriptor->status; @@ -804,8 +714,7 @@ UHCI::CheckDebugTransfer(DebugTransfer *transfer, bool &_stillPending) if (!transferOK && !transferError) { spin(200); - _stillPending = true; - return B_OK; + return B_DEV_PENDING; } if (transferOK) { @@ -815,29 +724,31 @@ UHCI::CheckDebugTransfer(DebugTransfer *transfer, bool &_stillPending) iovec *vector = transfer->Vector(); size_t vectorCount = transfer->VectorCount(); - ReadDescriptorChain(transfer->firstDescriptor, + ReadDescriptorChain(transferData->first_descriptor, vector, vectorCount, &lastDataToggle); } else { // read the actual length that was sent - ReadActualLength(transfer->firstDescriptor, &lastDataToggle); + ReadActualLength(transferData->first_descriptor, &lastDataToggle); } transfer->TransferPipe()->SetDataToggle(lastDataToggle == 0); } - fQueues[UHCI_DEBUG_QUEUE]->RemoveTransfer(transfer->transferQueue, false); - FreeDescriptorChain(transfer->firstDescriptor); - FreeTransferQueue(transfer->transferQueue); - _stillPending = false; + fQueues[UHCI_DEBUG_QUEUE]->RemoveTransfer(transferData->transfer_queue, + false); + FreeDescriptorChain(transferData->first_descriptor); + FreeTransferQueue(transferData->transfer_queue); return transferOK ? B_OK : B_IO_ERROR; } void -UHCI::CancelDebugTransfer(DebugTransfer *transfer) +UHCI::CancelDebugTransfer(Transfer *transfer) { + transfer_data *transferData = (transfer_data *)transfer->CallbackCookie(); + // clear the active bit so the descriptors are canceled - uhci_td *descriptor = transfer->firstDescriptor; + uhci_td *descriptor = transferData->first_descriptor; while (descriptor) { descriptor->status &= ~TD_STATUS_ACTIVE; descriptor = (uhci_td *)descriptor->link_log; @@ -846,9 +757,10 @@ UHCI::CancelDebugTransfer(DebugTransfer *transfer) transfer->Finished(B_CANCELED, 0); // dequeue and free resources - fQueues[UHCI_DEBUG_QUEUE]->RemoveTransfer(transfer->transferQueue, false); - FreeDescriptorChain(transfer->firstDescriptor); - FreeTransferQueue(transfer->transferQueue); + fQueues[UHCI_DEBUG_QUEUE]->RemoveTransfer(transferData->transfer_queue, + false); + FreeDescriptorChain(transferData->first_descriptor); + FreeTransferQueue(transferData->transfer_queue); // TODO: [bonefish] The Free*() calls cause "PMA: provided address resulted // in invalid index" to be printed, so apparently something is not right. // Though I have not clue what. This is the same cleanup code as in diff --git a/src/add-ons/kernel/busses/usb/uhci.h b/src/add-ons/kernel/busses/usb/uhci.h index f5fbb78d8d..d46d4809fb 100644 --- a/src/add-ons/kernel/busses/usb/uhci.h +++ b/src/add-ons/kernel/busses/usb/uhci.h @@ -24,8 +24,8 @@ struct pci_info; struct pci_module_info; struct pci_x86_module_info; + class UHCIRootHub; -class DebugTransfer; class Queue { @@ -99,11 +99,11 @@ public: status_t Start(); virtual status_t SubmitTransfer(Transfer *transfer); - status_t StartDebugTransfer(DebugTransfer *transfer); - status_t CheckDebugTransfer(DebugTransfer *transfer, - bool &_stillPending); - void CancelDebugTransfer( - DebugTransfer *transfer); + +virtual status_t StartDebugTransfer(Transfer *transfer); +virtual status_t CheckDebugTransfer(Transfer *transfer); +virtual void CancelDebugTransfer(Transfer *transfer); + virtual status_t CancelQueuedTransfers(Pipe *pipe, bool force); status_t CancelQueuedIsochronousTransfers(Pipe *pipe, bool force); status_t SubmitRequest(Transfer *transfer);