summaryrefslogtreecommitdiff
path: root/Kernel
diff options
context:
space:
mode:
authorb14ckcat <b14ckcat@protonmail.com>2022-10-17 01:22:54 -0400
committerAndrew Kaster <andrewdkaster@gmail.com>2022-11-12 09:08:02 -0700
commit7400eb36406f1e26ae55751aaa4c175630969402 (patch)
treec0215e58ea04f3c1825d1ecbe5bd75fc3c5a78c7 /Kernel
parent1aa16b4dd464e7c2b37e83eb077d1317479c8a9f (diff)
downloadserenity-7400eb36406f1e26ae55751aaa4c175630969402.zip
Kernel/USB: Add support for async & interrupt transfers
Add support for async transfers by using a separate kernel task to poll a list of active async transfers on a set time interval, and invoke their user-provided callback function when they are complete. Also add support for the interrupt class of transfers, building off of this async functionality.
Diffstat (limited to 'Kernel')
-rw-r--r--Kernel/Bus/USB/UHCI/UHCIController.cpp174
-rw-r--r--Kernel/Bus/USB/UHCI/UHCIController.h11
-rw-r--r--Kernel/Bus/USB/UHCI/UHCIDescriptorTypes.h16
-rw-r--r--Kernel/Bus/USB/USBController.h2
-rw-r--r--Kernel/Bus/USB/USBPipe.cpp16
-rw-r--r--Kernel/Bus/USB/USBPipe.h4
-rw-r--r--Kernel/Bus/USB/USBTransfer.cpp13
-rw-r--r--Kernel/Bus/USB/USBTransfer.h7
8 files changed, 181 insertions, 62 deletions
diff --git a/Kernel/Bus/USB/UHCI/UHCIController.cpp b/Kernel/Bus/USB/UHCI/UHCIController.cpp
index bb425e4743..17e0df4344 100644
--- a/Kernel/Bus/USB/UHCI/UHCIController.cpp
+++ b/Kernel/Bus/USB/UHCI/UHCIController.cpp
@@ -5,6 +5,7 @@
* SPDX-License-Identifier: BSD-2-Clause
*/
+#include <AK/Find.h>
#include <AK/Platform.h>
#include <Kernel/Arch/Delay.h>
#include <Kernel/Bus/PCI/API.h>
@@ -78,6 +79,7 @@ ErrorOr<void> UHCIController::initialize()
dmesgln("UHCI: I/O base {}", m_registers_io_window);
dmesgln("UHCI: Interrupt line: {}", interrupt_number());
+ TRY(spawn_async_poll_process());
TRY(spawn_port_process());
TRY(reset());
@@ -88,6 +90,7 @@ UNMAP_AFTER_INIT UHCIController::UHCIController(PCI::DeviceIdentifier const& pci
: PCI::Device(pci_device_identifier.address())
, IRQHandler(pci_device_identifier.interrupt_line().value())
, m_registers_io_window(move(registers_io_window))
+ , m_async_lock(LockRank::None)
, m_schedule_lock(LockRank::None)
{
}
@@ -132,14 +135,11 @@ UNMAP_AFTER_INIT ErrorOr<void> UHCIController::create_structures()
{
m_queue_head_pool = TRY(UHCIDescriptorPool<QueueHead>::try_create("Queue Head Pool"sv));
- // Used as a sentinel value to loop back to the beginning of the list
+ // Doesn't do anything other than give interrupt transfer queues something to set as prev QH so that we don't have to handle that as an extra edge case
m_schedule_begin_anchor = allocate_queue_head();
- // Each interrupt QH anchor in the array is linked into the schedule so that
- // it is executed once every (2^i) milliseconds, where i is it's index
- for (int i = 0; i < NUMBER_OF_INTERRUPT_QHS; i++) {
- m_interrupt_qh_anchor_arr[i] = allocate_queue_head();
- }
- // Create the Full Speed, Low Speed Control and Bulk Queue Heads
+
+ // Create the Interrupt, Full Speed, Low Speed Control and Bulk Queue Heads
+ m_interrupt_qh_anchor = allocate_queue_head();
m_ls_control_qh_anchor = allocate_queue_head();
m_fs_control_qh_anchor = allocate_queue_head();
m_bulk_qh_anchor = allocate_queue_head();
@@ -201,15 +201,11 @@ UNMAP_AFTER_INIT void UHCIController::setup_schedule()
// Not specified in the datasheet, however, is another Queue Head with an "inactive" Transfer Descriptor. This
// is to circumvent a bug in the silicon of the PIIX4's UHCI controller.
// https://github.com/openbsd/src/blob/master/sys/dev/usb/uhci.c#L390
- m_schedule_begin_anchor->link_next_queue_head(m_interrupt_qh_anchor_arr[0]);
+ m_schedule_begin_anchor->link_next_queue_head(m_interrupt_qh_anchor);
m_schedule_begin_anchor->terminate_element_link_ptr();
- for (int i = 0; i < NUMBER_OF_INTERRUPT_QHS - 1; i++) {
- m_interrupt_qh_anchor_arr[i]->link_next_queue_head(m_interrupt_qh_anchor_arr[i + 1]);
- m_interrupt_qh_anchor_arr[i]->terminate_element_link_ptr();
- }
- m_interrupt_qh_anchor_arr[NUMBER_OF_INTERRUPT_QHS - 1]->link_next_queue_head(m_ls_control_qh_anchor);
- m_interrupt_qh_anchor_arr[NUMBER_OF_INTERRUPT_QHS - 1]->terminate_element_link_ptr();
+ m_interrupt_qh_anchor->link_next_queue_head(m_ls_control_qh_anchor);
+ m_interrupt_qh_anchor->terminate_element_link_ptr();
m_ls_control_qh_anchor->link_next_queue_head(m_fs_control_qh_anchor);
m_ls_control_qh_anchor->terminate_element_link_ptr();
@@ -227,20 +223,12 @@ UNMAP_AFTER_INIT void UHCIController::setup_schedule()
u32* framelist = reinterpret_cast<u32*>(m_framelist->vaddr().as_ptr());
for (int frame_num = 0; frame_num < UHCI_NUMBER_OF_FRAMES; frame_num++) {
- auto frame_iso_td = m_iso_td_list.at(frame_num % UHCI_NUMBER_OF_ISOCHRONOUS_TDS);
- // Each frame pointer points to iso_td % NUM_ISO_TDS
- for (int i = NUMBER_OF_INTERRUPT_QHS - 1; i >= 0; i--) {
- if (frame_num % (1 << i) == 0) {
- frame_iso_td->link_queue_head(m_interrupt_qh_anchor_arr[i]->paddr());
- break;
- }
- }
+ auto& frame_iso_td = m_iso_td_list.at(frame_num % UHCI_NUMBER_OF_ISOCHRONOUS_TDS);
+ frame_iso_td->link_queue_head(m_schedule_begin_anchor->paddr());
framelist[frame_num] = frame_iso_td->paddr();
}
- for (int i = 0; i < NUMBER_OF_INTERRUPT_QHS; i++) {
- m_interrupt_qh_anchor_arr[i]->print();
- }
+ m_interrupt_qh_anchor->print();
m_ls_control_qh_anchor->print();
m_fs_control_qh_anchor->print();
m_bulk_qh_anchor->print();
@@ -387,6 +375,70 @@ void UHCIController::dequeue_qh(QueueHead* transfer_queue)
transfer_queue->prev_qh()->link_next_queue_head(transfer_queue->next_qh());
}
+ErrorOr<QueueHead*> UHCIController::create_transfer_queue(Transfer& transfer)
+{
+ Pipe& pipe = transfer.pipe();
+
+ // Create a new descriptor chain
+ TransferDescriptor* last_data_descriptor;
+ TransferDescriptor* data_descriptor_chain;
+ auto buffer_address = Ptr32<u8>(transfer.buffer_physical().as_ptr());
+ TRY(create_chain(pipe, transfer.pipe().direction() == Pipe::Direction::In ? PacketID::IN : PacketID::OUT, buffer_address, pipe.max_packet_size(), transfer.transfer_data_size(), &data_descriptor_chain, &last_data_descriptor));
+
+ last_data_descriptor->terminate();
+
+ if constexpr (UHCI_VERBOSE_DEBUG) {
+ if (data_descriptor_chain) {
+ dbgln("Data TD");
+ data_descriptor_chain->print();
+ }
+ }
+
+ QueueHead* transfer_queue = allocate_queue_head();
+ if (!transfer_queue) {
+ free_descriptor_chain(data_descriptor_chain);
+ return ENOMEM;
+ }
+
+ transfer_queue->attach_transfer_descriptor_chain(data_descriptor_chain);
+ transfer_queue->set_transfer(&transfer);
+
+ return transfer_queue;
+}
+
+ErrorOr<void> UHCIController::submit_async_transfer(NonnullOwnPtr<AsyncTransferHandle> async_handle, QueueHead* anchor, QueueHead* transfer_queue)
+{
+ {
+ SpinlockLocker locker { m_async_lock };
+ auto iter = find_if(m_active_async_transfers.begin(), m_active_async_transfers.end(), [](auto& handle) { return handle == nullptr; });
+ if (iter == m_active_async_transfers.end())
+ return ENOMEM;
+ *iter = move(async_handle);
+ }
+
+ enqueue_qh(transfer_queue, anchor);
+
+ return {};
+}
+
+void UHCIController::cancel_async_transfer(NonnullLockRefPtr<Transfer> transfer)
+{
+ SpinlockLocker locker { m_async_lock };
+
+ auto iter = find_if(m_active_async_transfers.begin(), m_active_async_transfers.end(), [transfer](auto& handle) { return handle != nullptr && handle->transfer.ptr() == transfer.ptr(); });
+ if (iter == m_active_async_transfers.end()) {
+ dbgln("Error: couldn't cancel supplied async transfer");
+ return; // We can't really do anything here, so just give up
+ }
+
+ auto& transfer_queue = (*iter)->qh;
+ dequeue_qh(transfer_queue);
+ free_descriptor_chain(transfer_queue->get_first_td());
+ transfer_queue->free();
+ m_queue_head_pool->release_to_pool(transfer_queue);
+ *iter = nullptr;
+}
+
ErrorOr<size_t> UHCIController::submit_control_transfer(Transfer& transfer)
{
Pipe& pipe = transfer.pipe(); // Short circuit the pipe related to this transfer
@@ -467,35 +519,11 @@ ErrorOr<size_t> UHCIController::submit_control_transfer(Transfer& transfer)
ErrorOr<size_t> UHCIController::submit_bulk_transfer(Transfer& transfer)
{
- Pipe& pipe = transfer.pipe();
- dbgln_if(UHCI_DEBUG, "UHCI: Received bulk transfer for address {}. Root Hub is at address {}.", pipe.device_address(), m_root_hub->device_address());
-
- // Create a new descriptor chain
- TransferDescriptor* last_data_descriptor;
- TransferDescriptor* data_descriptor_chain;
- auto buffer_address = Ptr32<u8>(transfer.buffer_physical().as_ptr());
- TRY(create_chain(pipe, transfer.pipe().direction() == Pipe::Direction::In ? PacketID::IN : PacketID::OUT, buffer_address, pipe.max_packet_size(), transfer.transfer_data_size(), &data_descriptor_chain, &last_data_descriptor));
-
- last_data_descriptor->terminate();
-
- if constexpr (UHCI_VERBOSE_DEBUG) {
- if (data_descriptor_chain) {
- dbgln("Data TD");
- data_descriptor_chain->print();
- }
- }
-
- QueueHead* transfer_queue = allocate_queue_head();
- if (!transfer_queue) {
- free_descriptor_chain(data_descriptor_chain);
- return ENOMEM;
- }
-
- transfer_queue->attach_transfer_descriptor_chain(data_descriptor_chain);
- transfer_queue->set_transfer(&transfer);
-
+ auto transfer_queue = TRY(create_transfer_queue(transfer));
enqueue_qh(transfer_queue, m_bulk_qh_anchor);
+ dbgln_if(UHCI_DEBUG, "UHCI: Received bulk transfer for address {}. Root Hub is at address {}.", transfer.pipe().device_address(), m_root_hub->device_address());
+
size_t transfer_size = 0;
while (!transfer.complete()) {
transfer_size = poll_transfer_queue(*transfer_queue);
@@ -510,6 +538,21 @@ ErrorOr<size_t> UHCIController::submit_bulk_transfer(Transfer& transfer)
return transfer_size;
}
+ErrorOr<void> UHCIController::submit_async_interrupt_transfer(NonnullLockRefPtr<Transfer> transfer, u16 ms_interval)
+{
+ dbgln_if(UHCI_DEBUG, "UHCI: Received interrupt transfer for address {}. Root Hub is at address {}.", transfer->pipe().device_address(), m_root_hub->device_address());
+
+ if (ms_interval == 0) {
+ return EINVAL;
+ }
+
+ auto transfer_queue = TRY(create_transfer_queue(*transfer));
+ auto async_transfer_handle = TRY(adopt_nonnull_own_or_enomem(new (nothrow) AsyncTransferHandle { transfer, transfer_queue, ms_interval }));
+ TRY(submit_async_transfer(move(async_transfer_handle), m_interrupt_qh_anchor, transfer_queue));
+
+ return {};
+}
+
size_t UHCIController::poll_transfer_queue(QueueHead& transfer_queue)
{
Transfer* transfer = transfer_queue.transfer();
@@ -556,6 +599,33 @@ ErrorOr<void> UHCIController::spawn_port_process()
return {};
}
+ErrorOr<void> UHCIController::spawn_async_poll_process()
+{
+ LockRefPtr<Thread> async_poll_thread;
+ (void)Process::create_kernel_process(async_poll_thread, TRY(KString::try_create("UHCI Async Poll Task"sv)), [&] {
+ u16 poll_interval_ms = 1024;
+ for (;;) {
+ {
+ SpinlockLocker locker { m_async_lock };
+ for (OwnPtr<AsyncTransferHandle>& handle : m_active_async_transfers) {
+ if (handle != nullptr) {
+ poll_interval_ms = min(poll_interval_ms, handle->ms_poll_interval);
+ QueueHead* qh = handle->qh;
+ for (auto td = qh->get_first_td(); td != nullptr && !td->active(); td = td->next_td()) {
+ if (td->next_td() == nullptr) { // Finished QH
+ handle->transfer->invoke_async_callback();
+ qh->reinitialize(); // Set the QH to be active again
+ }
+ }
+ }
+ }
+ }
+ (void)Thread::current()->sleep(Time::from_milliseconds(poll_interval_ms));
+ }
+ });
+ return {};
+}
+
bool UHCIController::handle_irq(RegisterState const&)
{
u32 status = read_usbsts();
diff --git a/Kernel/Bus/USB/UHCI/UHCIController.h b/Kernel/Bus/USB/UHCI/UHCIController.h
index 882e54bffd..ccbd218e6f 100644
--- a/Kernel/Bus/USB/UHCI/UHCIController.h
+++ b/Kernel/Bus/USB/UHCI/UHCIController.h
@@ -31,7 +31,6 @@ class UHCIController final
static constexpr u8 MAXIMUM_NUMBER_OF_TDS = 128; // Upper pool limit. This consumes the second page we have allocated
static constexpr u8 MAXIMUM_NUMBER_OF_QHS = 64;
- static constexpr u8 NUMBER_OF_INTERRUPT_QHS = 11;
public:
static constexpr u8 NUMBER_OF_ROOT_PORTS = 2;
@@ -44,10 +43,16 @@ public:
virtual ErrorOr<void> reset() override;
virtual ErrorOr<void> stop() override;
virtual ErrorOr<void> start() override;
+ ErrorOr<void> spawn_async_poll_process();
ErrorOr<void> spawn_port_process();
+ ErrorOr<QueueHead*> create_transfer_queue(Transfer& transfer);
+ ErrorOr<void> submit_async_transfer(NonnullOwnPtr<AsyncTransferHandle> async_handle, QueueHead* anchor, QueueHead* transfer_queue);
+
+ virtual void cancel_async_transfer(NonnullLockRefPtr<Transfer> transfer) override;
virtual ErrorOr<size_t> submit_control_transfer(Transfer& transfer) override;
virtual ErrorOr<size_t> submit_bulk_transfer(Transfer& transfer) override;
+ virtual ErrorOr<void> submit_async_interrupt_transfer(NonnullLockRefPtr<Transfer> transfer, u16 ms_interval) override;
void get_port_status(Badge<UHCIRootHub>, u8, HubStatus&);
ErrorOr<void> set_port_feature(Badge<UHCIRootHub>, u8, HubFeatureSelector);
@@ -95,15 +100,17 @@ private:
NonnullOwnPtr<IOWindow> m_registers_io_window;
+ Spinlock m_async_lock;
Spinlock m_schedule_lock;
OwnPtr<UHCIRootHub> m_root_hub;
OwnPtr<UHCIDescriptorPool<QueueHead>> m_queue_head_pool;
OwnPtr<UHCIDescriptorPool<TransferDescriptor>> m_transfer_descriptor_pool;
Vector<TransferDescriptor*> m_iso_td_list;
+ Array<OwnPtr<AsyncTransferHandle>, MAXIMUM_NUMBER_OF_QHS> m_active_async_transfers;
QueueHead* m_schedule_begin_anchor;
- Array<QueueHead*, NUMBER_OF_INTERRUPT_QHS> m_interrupt_qh_anchor_arr;
+ QueueHead* m_interrupt_qh_anchor;
QueueHead* m_ls_control_qh_anchor;
QueueHead* m_fs_control_qh_anchor;
// Always final queue in the schedule, may loop back to previous QH for bandwidth
diff --git a/Kernel/Bus/USB/UHCI/UHCIDescriptorTypes.h b/Kernel/Bus/USB/UHCI/UHCIDescriptorTypes.h
index 6375af1227..8dab52c99c 100644
--- a/Kernel/Bus/USB/UHCI/UHCIDescriptorTypes.h
+++ b/Kernel/Bus/USB/UHCI/UHCIDescriptorTypes.h
@@ -356,6 +356,15 @@ struct alignas(16) QueueHead {
m_bookkeeping->in_use = false;
}
+ void reinitialize()
+ {
+
+ for (TransferDescriptor* iter = get_first_td(); iter != nullptr; iter = iter->next_td()) {
+ iter->set_active();
+ }
+ attach_transfer_descriptor_chain(get_first_td());
+ }
+
private:
u32 m_link_ptr { 0 }; // Pointer to the next horizontal object that the controller will execute after this one
volatile u32 m_element_link_ptr { 0 }; // Pointer to the first data object in the queue (can be modified by hw)
@@ -366,4 +375,11 @@ private:
};
static_assert(AssertSize<QueueHead, 32>()); // Queue Head is always 8 Dwords
+
+struct AsyncTransferHandle {
+ NonnullLockRefPtr<Transfer> transfer;
+ QueueHead* qh;
+ u16 ms_poll_interval;
+};
+
}
diff --git a/Kernel/Bus/USB/USBController.h b/Kernel/Bus/USB/USBController.h
index 47fc1d1d6d..73bb01d9c3 100644
--- a/Kernel/Bus/USB/USBController.h
+++ b/Kernel/Bus/USB/USBController.h
@@ -23,8 +23,10 @@ public:
virtual ErrorOr<void> stop() = 0;
virtual ErrorOr<void> start() = 0;
+ virtual void cancel_async_transfer(NonnullLockRefPtr<Transfer> transfer) = 0;
virtual ErrorOr<size_t> submit_control_transfer(Transfer&) = 0;
virtual ErrorOr<size_t> submit_bulk_transfer(Transfer& transfer) = 0;
+ virtual ErrorOr<void> submit_async_interrupt_transfer(NonnullLockRefPtr<Transfer> transfer, u16 ms_interval) = 0;
u8 allocate_address();
diff --git a/Kernel/Bus/USB/USBPipe.cpp b/Kernel/Bus/USB/USBPipe.cpp
index ac4c79bc57..9a97b6e608 100644
--- a/Kernel/Bus/USB/USBPipe.cpp
+++ b/Kernel/Bus/USB/USBPipe.cpp
@@ -50,7 +50,7 @@ ErrorOr<size_t> ControlPipe::control_transfer(u8 request_type, u8 request, u16 v
usb_request.index = index;
usb_request.length = length;
- auto transfer = TRY(Transfer::try_create(*this, length, *m_dma_buffer));
+ auto transfer = TRY(Transfer::create(*this, length, *m_dma_buffer));
transfer->set_setup_packet(usb_request);
dbgln_if(USB_DEBUG, "ControlPipe: Transfer allocated @ {}", transfer->buffer_physical());
@@ -84,7 +84,7 @@ ErrorOr<size_t> BulkInPipe::bulk_in_transfer(size_t length, void* data)
size_t transfer_length = 0;
- auto transfer = TRY(Transfer::try_create(*this, length, *m_dma_buffer));
+ auto transfer = TRY(Transfer::create(*this, length, *m_dma_buffer));
dbgln_if(USB_DEBUG, "Pipe: Bulk in transfer allocated @ {}", transfer->buffer_physical());
transfer_length = TRY(m_controller->submit_bulk_transfer(*transfer));
@@ -114,7 +114,7 @@ ErrorOr<size_t> BulkOutPipe::bulk_out_transfer(size_t length, void* data)
MutexLocker lock(m_dma_buffer_lock);
size_t transfer_length = 0;
- auto transfer = TRY(Transfer::try_create(*this, length, *m_dma_buffer));
+ auto transfer = TRY(Transfer::create(*this, length, *m_dma_buffer));
TRY(transfer->write_buffer(length, data));
dbgln_if(USB_DEBUG, "Pipe: Bulk out transfer allocated @ {}", transfer->buffer_physical());
@@ -137,6 +137,16 @@ InterruptInPipe::InterruptInPipe(USBController const& controller, u8 endpoint_ad
{
}
+ErrorOr<NonnullLockRefPtr<Transfer>> InterruptInPipe::interrupt_in_transfer(size_t length, u16 ms_interval, USBAsyncCallback callback)
+{
+ VERIFY(length <= m_dma_buffer->size());
+
+ auto transfer = TRY(Transfer::create(*this, length, *m_dma_buffer, move(callback)));
+ dbgln_if(USB_DEBUG, "Pipe: Interrupt in transfer allocated @ {}", transfer->buffer_physical());
+ TRY(m_controller->submit_async_interrupt_transfer(transfer, ms_interval));
+ return transfer;
+}
+
ErrorOr<NonnullOwnPtr<InterruptOutPipe>> InterruptOutPipe::create(USBController const& controller, u8 endpoint_address, u16 max_packet_size, i8 device_address, u16 poll_interval, size_t buffer_size)
{
VERIFY(buffer_size >= max_packet_size);
diff --git a/Kernel/Bus/USB/USBPipe.h b/Kernel/Bus/USB/USBPipe.h
index 578852dc0f..6ccb6a205b 100644
--- a/Kernel/Bus/USB/USBPipe.h
+++ b/Kernel/Bus/USB/USBPipe.h
@@ -18,6 +18,8 @@ namespace Kernel::USB {
class USBController;
class Transfer;
+using USBAsyncCallback = Function<void(Transfer* transfer)>;
+
//
// A pipe is the logical connection between a memory buffer on the PC (host) and
// an endpoint on the device. In this implementation, the data buffer the pipe connects
@@ -111,6 +113,8 @@ class InterruptInPipe : public Pipe {
public:
static ErrorOr<NonnullOwnPtr<InterruptInPipe>> create(USBController const& controller, u8 endpoint_address, u16 max_packet_size, i8 device_address, u16 poll_interval, size_t buffer_size = PAGE_SIZE);
+ ErrorOr<NonnullLockRefPtr<Transfer>> interrupt_in_transfer(size_t length, u16 ms_interval, USBAsyncCallback callback);
+
u16 poll_interval() const { return m_poll_interval; }
private:
diff --git a/Kernel/Bus/USB/USBTransfer.cpp b/Kernel/Bus/USB/USBTransfer.cpp
index e3a0e75d3b..ada524d4f0 100644
--- a/Kernel/Bus/USB/USBTransfer.cpp
+++ b/Kernel/Bus/USB/USBTransfer.cpp
@@ -9,15 +9,16 @@
namespace Kernel::USB {
-ErrorOr<NonnullLockRefPtr<Transfer>> Transfer::try_create(Pipe& pipe, u16 length, Memory::Region& dma_buffer)
+ErrorOr<NonnullLockRefPtr<Transfer>> Transfer::create(Pipe& pipe, u16 length, Memory::Region& dma_buffer, USBAsyncCallback callback)
{
- return adopt_nonnull_lock_ref_or_enomem(new (nothrow) Transfer(pipe, length, dma_buffer));
+ return adopt_nonnull_lock_ref_or_enomem(new (nothrow) Transfer(pipe, length, dma_buffer, move(callback)));
}
-Transfer::Transfer(Pipe& pipe, u16 len, Memory::Region& dma_buffer)
+Transfer::Transfer(Pipe& pipe, u16 len, Memory::Region& dma_buffer, USBAsyncCallback callback)
: m_pipe(pipe)
, m_dma_buffer(dma_buffer)
, m_transfer_data_size(len)
+ , m_callback(move(callback))
{
}
@@ -49,4 +50,10 @@ ErrorOr<void> Transfer::write_buffer(u16 len, void* data)
return {};
}
+void Transfer::invoke_async_callback()
+{
+ if (m_callback)
+ m_callback(this);
+}
+
}
diff --git a/Kernel/Bus/USB/USBTransfer.h b/Kernel/Bus/USB/USBTransfer.h
index 5923542d6c..c506650d9e 100644
--- a/Kernel/Bus/USB/USBTransfer.h
+++ b/Kernel/Bus/USB/USBTransfer.h
@@ -20,7 +20,7 @@ namespace Kernel::USB {
class Transfer final : public AtomicRefCounted<Transfer> {
public:
- static ErrorOr<NonnullLockRefPtr<Transfer>> try_create(Pipe&, u16 length, Memory::Region& dma_buffer);
+ static ErrorOr<NonnullLockRefPtr<Transfer>> create(Pipe&, u16 length, Memory::Region& dma_buffer, USBAsyncCallback callback = nullptr);
Transfer() = delete;
~Transfer();
@@ -41,14 +41,17 @@ public:
bool complete() const { return m_complete; }
bool error_occurred() const { return m_error_occurred; }
+ void invoke_async_callback();
+
private:
- Transfer(Pipe& pipe, u16 len, Memory::Region& dma_buffer);
+ Transfer(Pipe& pipe, u16 len, Memory::Region& dma_buffer, USBAsyncCallback callback);
Pipe& m_pipe; // Pipe that initiated this transfer
Memory::Region& m_dma_buffer; // DMA buffer
USBRequestData m_request; // USB request
u16 m_transfer_data_size { 0 }; // Size of the transfer's data stage
bool m_complete { false }; // Has this transfer been completed?
bool m_error_occurred { false }; // Did an error occur during this transfer?
+ USBAsyncCallback m_callback { nullptr };
};
}