[dev][usb-bus] Use C++ USB request library
TEST: USB works on NUC and in qemu with USB virtual bus
Change-Id: I98aeff7c3c0ae7bbdc6a6620261286da7271d5a1
diff --git a/system/dev/usb/usb-bus/rules.mk b/system/dev/usb/usb-bus/rules.mk
index 9370ceb..e4ef1a1 100644
--- a/system/dev/usb/usb-bus/rules.mk
+++ b/system/dev/usb/usb-bus/rules.mk
@@ -19,7 +19,9 @@
system/ulib/fidl \
system/ulib/sync \
system/ulib/utf_conversion \
+ system/dev/lib/operation \
system/dev/lib/usb \
+ system/ulib/zx \
system/ulib/zxcpp \
MODULE_LIBS := \
diff --git a/system/dev/usb/usb-bus/usb-bus.h b/system/dev/usb/usb-bus/usb-bus.h
index c8675d5..4f6a08e 100644
--- a/system/dev/usb/usb-bus/usb-bus.h
+++ b/system/dev/usb/usb-bus/usb-bus.h
@@ -43,6 +43,8 @@
zx_status_t UsbBusInterfaceReinitializeDevice(uint32_t device_id);
private:
+ DISALLOW_COPY_ASSIGN_AND_MOVE(UsbBus);
+
zx_status_t Init();
zx_status_t GetDeviceId(zx_device_t* device, uint32_t* out);
@@ -53,5 +55,4 @@
fbl::Array<fbl::RefPtr<UsbDevice>> devices_;
};
-
} // namespace usb_bus
diff --git a/system/dev/usb/usb-bus/usb-device.cpp b/system/dev/usb/usb-bus/usb-device.cpp
index ef53a3e..e29df11 100644
--- a/system/dev/usb/usb-bus/usb-device.cpp
+++ b/system/dev/usb/usb-bus/usb-device.cpp
@@ -19,16 +19,6 @@
namespace usb_bus {
-struct UsbRequestInternal {
- // callback to client driver
- usb_request_complete_t complete_cb;
- // for queueing at the usb-bus level
- list_node_t node;
-};
-#define USB_REQ_TO_DEV_INTERNAL(req, size) \
- ((UsbRequestInternal *)((uintptr_t)(req) + (size)))
-#define DEV_INTERNAL_TO_USB_REQ(ctx, size) ((usb_request_t *)((uintptr_t)(ctx) - (size)))
-
// By default we create devices for the interfaces on the first configuration.
// This table allows us to specify a different configuration for certain devices
// based on their VID and PID.
@@ -55,7 +45,7 @@
bool done = false;
while (!done) {
- list_node_t temp_list = LIST_INITIAL_VALUE(temp_list);
+ UnownedRequestQueue temp_queue;
// Wait for new usb requests to complete or for signal to exit this thread.
sync_completion_wait(&callback_thread_completion_, ZX_TIME_INFINITE);
@@ -67,16 +57,13 @@
done = callback_thread_stop_;
// Copy completed requests to a temp list so we can process them outside of our lock.
- list_move(&completed_reqs_, &temp_list);
+ temp_queue = std::move(completed_reqs_);
}
// Call completion callbacks outside of the lock.
- usb_request_t* req;
- UsbRequestInternal* req_int;
- while ((req_int = list_remove_head_type(&temp_list, UsbRequestInternal, node))) {
- req = DEV_INTERNAL_TO_USB_REQ(req_int, parent_req_size_);
- usb_request_complete(req, req->response.status, req->response.actual,
- &req_int->complete_cb);
+ for (auto req = temp_queue.pop(); req; req = temp_queue.pop()) {
+ const auto& response = req->request()->response;
+ req->Complete(response.status, response.actual);
}
}
@@ -111,8 +98,7 @@
}
// move original request to completed_reqs list so it can be completed on the callback_thread
- UsbRequestInternal* req_int = USB_REQ_TO_DEV_INTERNAL(req, parent_req_size_);
- list_add_tail(&completed_reqs_, &req_int->node);
+ completed_reqs_.push(UnownedRequest(req, parent_req_size_));
sync_completion_signal(&callback_thread_completion_);
}
@@ -176,22 +162,21 @@
return ZX_ERR_OUT_OF_RANGE;
}
- usb_request_t* req = NULL;
+ std::optional<Request> req;
bool use_free_list = (length == 0);
if (use_free_list) {
- fbl::AutoLock lock(&free_reqs_lock_);
- req = usb_request_pool_get(&free_reqs_, length);
+ req = free_reqs_.Get(length);
}
- if (req == NULL) {
- auto status = usb_request_alloc(&req, length, 0, req_size_);
+ if (!req.has_value()) {
+ auto status = Request::Alloc(&req, length, 0, parent_req_size_);
if (status != ZX_OK) {
return status;
}
}
// fill in protocol data
- usb_setup_t* setup = &req->setup;
+ usb_setup_t* setup = &req->request()->setup;
setup->bmRequestType = request_type;
setup->bRequest = request;
setup->wValue = value;
@@ -199,14 +184,14 @@
setup->wLength = static_cast<uint16_t>(length);
if (out) {
- if (length > 0 && write_buffer == NULL) {
+ if (length > 0 && write_buffer == nullptr) {
return ZX_ERR_INVALID_ARGS;
}
if (length > write_size) {
return ZX_ERR_BUFFER_TOO_SMALL;
}
} else {
- if (length > 0 && out_read_buffer == NULL) {
+ if (length > 0 && out_read_buffer == nullptr) {
return ZX_ERR_INVALID_ARGS;
}
if (length > read_size) {
@@ -215,24 +200,25 @@
}
if (length > 0 && out) {
- usb_request_copy_to(req, write_buffer, length, 0);
+ req->CopyTo(write_buffer, length, 0);
}
sync_completion_t completion;
- req->header.device_id = device_id_;
- req->header.length = length;
+ req->request()->header.device_id = device_id_;
+ req->request()->header.length = length;
// We call this directly instead of via hci_queue, as it's safe to call our
// own completion callback, and prevents clients getting into odd deadlocks.
usb_request_complete_t complete = {
.callback = ControlComplete,
.ctx = &completion,
};
- hci_.RequestQueue(req, &complete);
+ // Use request() instead of take() since we keep referring to the request below.
+ hci_.RequestQueue(req->request(), &complete);
auto status = sync_completion_wait(&completion, timeout);
if (status == ZX_OK) {
- status = req->response.status;
+ status = req->request()->response.status;
} else if (status == ZX_ERR_TIMED_OUT) {
// cancel transactions and wait for request to be completed
sync_completion_reset(&completion);
@@ -243,22 +229,19 @@
}
}
if (status == ZX_OK && !out) {
+ auto actual = req->request()->response.actual;
if (length > 0) {
- usb_request_copy_from(req, out_read_buffer, req->response.actual, 0);
+ req->CopyFrom(out_read_buffer, actual, 0);
}
- if (out_read_actual != NULL) {
- *out_read_actual = req->response.actual;
+ if (out_read_actual != nullptr) {
+ *out_read_actual = actual;
}
}
if (use_free_list) {
- fbl::AutoLock lock(&free_reqs_lock_);
- if (usb_request_pool_add(&free_reqs_, req) != ZX_OK) {
- zxlogf(TRACE, "Unable to add back request to the free pool\n");
- usb_request_release(req);
- }
+ free_reqs_.Add(std::move(*req));
} else {
- usb_request_release(req);
+ req->Release();
}
return status;
}
@@ -284,19 +267,22 @@
}
void UsbDevice::UsbRequestQueue(usb_request_t* req, const usb_request_complete_t* complete_cb) {
- UsbRequestInternal* req_int = USB_REQ_TO_DEV_INTERNAL(req, parent_req_size_);
- req_int->complete_cb = *complete_cb;
-
req->header.device_id = device_id_;
- // save the existing callback, so we can replace them
- // with our own before passing the request to the HCI driver.
+
+ // Queue to HCI driver with our own completion callback so we can call client's completion
+ // on our own thread, to avoid drivers from deadlocking the HCI driver's interrupt thread.
usb_request_complete_t complete = {
.callback = [](void* ctx, usb_request_t* req) {
static_cast<UsbDevice*>(ctx)->RequestComplete(req);
},
.ctx = this,
};
- hci_.RequestQueue(req, &complete);
+
+ // Save client's callback in private storage.
+ UnownedRequest request(req, *complete_cb, parent_req_size_);
+
+ // Queue with our callback instead.
+ hci_.RequestQueue(request.take(), &complete);
}
usb_speed_t UsbDevice::UsbGetSpeed() {
@@ -541,7 +527,7 @@
}
size_t UsbDevice::UsbGetRequestSize() {
- return req_size_;
+ return UnownedRequest::RequestSize(parent_req_size_);
}
zx_status_t UsbDevice::MsgGetDeviceSpeed(fidl_txn_t* txn) {
@@ -680,10 +666,6 @@
ddk_proto_id_ = ZX_PROTOCOL_USB_DEVICE;
parent_req_size_ = hci_.GetRequestSize();
- req_size_ = parent_req_size_ + sizeof(UsbRequestInternal);
-
- list_initialize(&completed_reqs_);
- usb_request_pool_init(&free_reqs_, parent_req_size_ + offsetof(UsbRequestInternal, node));
// read device descriptor
size_t actual;
diff --git a/system/dev/usb/usb-bus/usb-device.h b/system/dev/usb/usb-bus/usb-device.h
index a4083dc..193cf14 100644
--- a/system/dev/usb/usb-bus/usb-device.h
+++ b/system/dev/usb/usb-bus/usb-device.h
@@ -14,6 +14,7 @@
#include <fbl/ref_ptr.h>
#include <usb/usb-request.h>
#include <lib/sync/completion.h>
+#include <usb/request-cpp.h>
#include <zircon/hw/usb.h>
#include <optional>
@@ -97,6 +98,12 @@
inline usb_speed_t GetSpeed() const { return speed_; }
private:
+ DISALLOW_COPY_ASSIGN_AND_MOVE(UsbDevice);
+
+ using Request = usb::Request<void>;
+ using UnownedRequest = usb::UnownedRequest<void>;
+ using UnownedRequestQueue = usb::UnownedRequestQueue<void>;
+
zx_status_t Init();
int CallbackThread();
@@ -140,16 +147,14 @@
// completion used for signalling callback_thread
sync_completion_t callback_thread_completion_;
// list of requests that need to have client's completion callback called
- list_node_t completed_reqs_ __TA_GUARDED(callback_lock_);
+ UnownedRequestQueue completed_reqs_ __TA_GUARDED(callback_lock_);
// mutex that protects the callback_* members above
fbl::Mutex callback_lock_;
- // pool of requests that can be reused
- usb_request_pool_t free_reqs_ __TA_GUARDED(free_reqs_lock_);
- fbl::Mutex free_reqs_lock_;
+ // Pool of requests USB control requests with zero data.
+ usb::RequestPool<void> free_reqs_;
size_t parent_req_size_;
- size_t req_size_;
};
} // namespace usb_bus