[usb-virtual-bus] Use C++ usb::Request library.

Tested: Ran following in QEMu:
usbctl virtual enable
usbctl init-ums
usbctl virtual connect
mkfs /dev/class/block/000 minfs
mkdir /tmp/mnt
mount /dev/class/block/000 /tmp/mnt
kilo /tmp/mnt/foo
(type some text, save and quit)
cat /tmp/mnt/foo

Change-Id: If0f49e6ee8e437776a64428e3f730bbb414b7449
diff --git a/system/dev/lib/usb/include/usb/request-cpp.h b/system/dev/lib/usb/include/usb/request-cpp.h
index 1ef9194..3d2c321 100644
--- a/system/dev/lib/usb/include/usb/request-cpp.h
+++ b/system/dev/lib/usb/include/usb/request-cpp.h
@@ -500,6 +500,13 @@
         __UNUSED auto dummy = req.take();
     }
 
+    void push_next(ReqType req) {
+        fbl::AutoLock al(&lock_);
+        auto* node = req.node();
+        queue_.push_back(node);
+        __UNUSED auto dummy = req.take();
+    }
+
     std::optional<ReqType> pop() {
         fbl::AutoLock al(&lock_);
         auto* node = queue_.pop_back();
@@ -509,6 +516,11 @@
         return std::nullopt;
     }
 
+    bool is_empty() {
+        fbl::AutoLock al(&lock_);
+        return queue_.is_empty();
+    }
+
     // Releases all usb requests stored in the queue.
     void release() {
         fbl::AutoLock al(&lock_);
diff --git a/system/dev/usb/usb-virtual-bus/usb-virtual-bus.cpp b/system/dev/usb/usb-virtual-bus/usb-virtual-bus.cpp
index 5a04d44..669a158 100644
--- a/system/dev/usb/usb-virtual-bus/usb-virtual-bus.cpp
+++ b/system/dev/usb/usb-virtual-bus/usb-virtual-bus.cpp
@@ -17,7 +17,6 @@
 #include <fbl/auto_lock.h>
 #include <fbl/unique_ptr.h>
 #include <fuchsia/usb/virtualbus/c/fidl.h>
-#include <usb/usb-request.h>
 
 namespace usb_virtual_bus {
 
@@ -28,18 +27,6 @@
 }
 static constexpr uint8_t IN_EP_START = 17;
 
-// Internal context for USB requests, used for both host and peripheral side.
-struct UsbReqInternal {
-     // Callback to the upper layer.
-     usb_request_complete_t complete_cb;
-     // For queueing requests internally.
-     list_node_t node;
-};
-
-#define USB_REQ_TO_INTERNAL(req) \
-    ((UsbReqInternal *)((uintptr_t)(req) + sizeof(usb_request_t)))
-#define INTERNAL_TO_USB_REQ(ctx) ((usb_request_t *)((uintptr_t)(ctx) - sizeof(usb_request_t)))
-
 #define DEVICE_SLOT_ID  0
 #define DEVICE_HUB_ID   0
 #define DEVICE_SPEED    USB_SPEED_HIGH
@@ -94,12 +81,6 @@
 }
 
 zx_status_t UsbVirtualBus::Init() {
-    for (unsigned i = 0; i < USB_MAX_EPS; i++) {
-        usb_virtual_ep_t* ep = &eps_[i];
-        list_initialize(&ep->host_reqs);
-        list_initialize(&ep->device_reqs);
-    }
-
     auto status = DdkAdd("usb-virtual-bus", DEVICE_ADD_NON_BINDABLE);
     if (status != ZX_OK) {
         return status;
@@ -120,123 +101,84 @@
 
 int UsbVirtualBus::Thread() {
     while (1) {
-        UsbReqInternal* req_int;
-        list_node_t completed = LIST_INITIAL_VALUE(completed);
-
         sync_completion_wait(&thread_signal_, ZX_TIME_INFINITE);
         sync_completion_reset(&thread_signal_);
 
-        lock_.Acquire();
-
-        if (unbinding_) {
+        bool unbinding;
+        {
+            fbl::AutoLock al(&lock_);
+            unbinding = unbinding_;
+        }
+        if (unbinding) {
             for (unsigned i = 0; i < USB_MAX_EPS; i++) {
                 usb_virtual_ep_t* ep = &eps_[i];
 
-                while ((req_int = list_remove_head_type(&ep->host_reqs, UsbReqInternal,
-                                                        node)) != nullptr) {
-                    list_add_tail(&completed, &req_int->node);
+                for (auto req = ep->host_reqs.pop(); req; req = ep->host_reqs.pop()) {
+                    req->Complete(ZX_ERR_IO_NOT_PRESENT, 0);
                 }
-                while ((req_int = list_remove_head_type(&ep->device_reqs, UsbReqInternal,
-                                                        node)) != nullptr) {
-                    list_add_tail(&completed, &req_int->node);
+                for (auto req = ep->device_reqs.pop(); req; req = ep->device_reqs.pop()) {
+                    req->Complete(ZX_ERR_IO_NOT_PRESENT, 0);
                 }
             }
 
-            lock_.Release();
-
-            // Complete requests outside of the lock to avoid deadlock.
-            while ((req_int = list_remove_head_type(&completed, UsbReqInternal, node))
-                    != nullptr) {
-                usb_request_t* req = INTERNAL_TO_USB_REQ(req_int);
-                usb_request_complete(req, ZX_ERR_IO_NOT_PRESENT, 0, &req_int->complete_cb);
-            }
-
             return 0;
         }
 
         // special case endpoint zero
-        while ((req_int = list_remove_head_type(&eps_[0].host_reqs, UsbReqInternal, node))
-                != nullptr) {
-            lock_.Release();
+        for (auto request = eps_[0].host_reqs.pop(); request; request = eps_[0].host_reqs.pop()) {
             // Handle control requests outside of the lock to avoid deadlock.
-            HandleControl(INTERNAL_TO_USB_REQ(req_int));
-            lock_.Acquire();
+            HandleControl(std::move(*request));
         }
 
         for (unsigned i = 1; i < USB_MAX_EPS; i++) {
             usb_virtual_ep_t* ep = &eps_[i];
             bool out = (i < IN_EP_START);
 
-            while ((req_int = list_peek_head_type(&ep->host_reqs, UsbReqInternal, node))
-                    != nullptr) {
-                UsbReqInternal* device_req_int;
-                device_req_int = list_remove_head_type(&ep->device_reqs, UsbReqInternal, node);
+            while (!ep->host_reqs.is_empty() && !ep->device_reqs.is_empty()) {
+                auto device_req = ep->device_reqs.pop();
+                auto req = ep->host_reqs.pop();
 
-                if (device_req_int) {
-                    usb_request_t* req = INTERNAL_TO_USB_REQ(req_int);
-                    usb_request_t* device_req = INTERNAL_TO_USB_REQ(device_req_int);
-                    zx_off_t offset = ep->req_offset;
-                    size_t length = req->header.length - offset;
-                    if (length > device_req->header.length) {
-                        length = device_req->header.length;
-                    }
-
-                    void* device_buffer;
-                    auto status = usb_request_mmap(device_req, &device_buffer);
-                    if (status != ZX_OK) {
-                        zxlogf(ERROR, "%s: usb_request_mmap failed: %d\n", __func__, status);
-                        req->response.status = status;
-                        device_req->response.status = status;
-                        list_add_tail(&completed, &device_req_int->node);
-                        list_add_tail(&completed, &req_int->node);
-                        continue;
-                    }
-
-                    if (out) {
-                        usb_request_copy_from(req, device_buffer, length, offset);
-                    } else {
-                        usb_request_copy_to(req, device_buffer, length, offset);
-                    }
-
-                    device_req->response.status = ZX_OK;
-                    device_req->response.actual = length;
-                    list_add_tail(&completed, &device_req_int->node);
-
-                    offset += length;
-                    if (offset == req->header.length ||
-                        // Short packet in the IN direction signals end of transfer.
-                        (!out && device_req->header.length < ep->max_packet_size)) {
-                        list_delete(&req_int->node);
-                        usb_request_t* req = INTERNAL_TO_USB_REQ(req_int);
-                        req->response.status = ZX_OK;
-                        req->response.actual = length;
-                        list_add_tail(&completed, &req_int->node);
-                        ep->req_offset = 0;
-                    } else {
-                        ep->req_offset = offset;
-                    }
-                } else {
-                    break;
+                zx_off_t offset = ep->req_offset;
+                size_t length = req->request()->header.length - offset;
+                if (length > device_req->request()->header.length) {
+                    length = device_req->request()->header.length;
                 }
+
+                void* device_buffer;
+                auto status = device_req->Mmap(&device_buffer);
+                if (status != ZX_OK) {
+                    zxlogf(ERROR, "%s: usb_request_mmap failed: %d\n", __func__, status);
+                    req->Complete(status, 0);
+                    device_req->Complete(status, 0);
+                    continue;
+                }
+
+                if (out) {
+                    req->CopyFrom(device_buffer, length, offset);
+                } else {
+                    req->CopyTo(device_buffer, length, offset);
+                }
+
+                offset += length;
+                if (offset == req->request()->header.length ||
+                    // Short packet in the IN direction signals end of transfer.
+                    (!out && device_req->request()->header.length < ep->max_packet_size)) {
+                    req->Complete(ZX_OK, length);
+                    ep->req_offset = 0;
+                } else {
+                    ep->req_offset = offset;
+                    ep->host_reqs.push_next(std::move(*req));
+                }
+
+                device_req->Complete(ZX_OK, length);
             }
         }
-
-        lock_.Release();
-
-        // Complete requests outside of the lock to avoid deadlock.
-        while ((req_int = list_remove_head_type(&completed, UsbReqInternal, node))
-                != nullptr) {
-            usb_request_t* req = INTERNAL_TO_USB_REQ(req_int);
-            usb_request_complete(req, req->response.status, req->response.actual,
-                                 &req_int->complete_cb);
-        }
     }
     return 0;
 }
 
-void UsbVirtualBus::HandleControl(usb_request_t* req) {
-    const usb_setup_t* setup = &req->setup;
-    auto* req_int = USB_REQ_TO_INTERNAL(req);
+void UsbVirtualBus::HandleControl(Request request) {
+    const usb_setup_t* setup = &request.request()->setup;
     zx_status_t status;
     size_t length = le16toh(setup->wLength);
     size_t actual = 0;
@@ -249,10 +191,10 @@
         void* buffer = nullptr;
 
         if (length > 0) {
-            auto status = usb_request_mmap(req, &buffer);
+            auto status = request.Mmap(&buffer);
             if (status != ZX_OK) {
                 zxlogf(ERROR, "%s: usb_request_mmap failed: %d\n", __func__, status);
-                usb_request_complete(req, status, 0, &req_int->complete_cb);
+                request.Complete(status, 0);
                 return;
             }
         }
@@ -266,7 +208,7 @@
         status = ZX_ERR_UNAVAILABLE;
     }
 
-    usb_request_complete(req, status, actual, &req_int->complete_cb);
+    request.Complete(status, actual);
 }
 
 void UsbVirtualBus::SetConnected(bool connected) {
@@ -291,30 +233,16 @@
             dci_intf_.SetConnected(false);
         }
 
-        UsbReqInternal* req_int;
-        list_node_t completed = LIST_INITIAL_VALUE(completed);
 
-        {
-            fbl::AutoLock lock(&lock_);
+        for (unsigned i = 0; i < USB_MAX_EPS; i++) {
+            usb_virtual_ep_t* ep = &eps_[i];
 
-            for (unsigned i = 0; i < USB_MAX_EPS; i++) {
-                usb_virtual_ep_t* ep = &eps_[i];
-
-                while ((req_int = list_remove_head_type(&ep->host_reqs, UsbReqInternal,
-                                                        node)) != nullptr) {
-                    list_add_tail(&completed, &req_int->node);
-                }
-                while ((req_int = list_remove_head_type(&ep->device_reqs, UsbReqInternal,
-                                                        node)) != nullptr) {
-                    list_add_tail(&completed, &req_int->node);
-                }
+            for (auto req = ep->host_reqs.pop(); req; req = ep->host_reqs.pop()) {
+                req->Complete(ZX_ERR_IO_NOT_PRESENT, 0);
             }
-        }
-
-        while ((req_int = list_remove_head_type(&completed, UsbReqInternal, node))
-                != nullptr) {
-            usb_request_t* req = INTERNAL_TO_USB_REQ(req_int);
-            usb_request_complete(req, ZX_ERR_IO_NOT_PRESENT, 0, &req_int->complete_cb);
+            for (auto req = ep->device_reqs.pop(); req; req = ep->device_reqs.pop()) {
+                req->Complete(ZX_ERR_IO_NOT_PRESENT, 0);
+            }
         }
     }
 }
@@ -325,7 +253,7 @@
         return ZX_ERR_INVALID_ARGS;
     }
 
-    UsbReqInternal* req_int = nullptr;
+    std::optional<Request> req = std::nullopt;
     {
         fbl::AutoLock lock(&lock_);
 
@@ -333,13 +261,12 @@
         ep->stalled = stall;
 
         if (stall) {
-            req_int = list_remove_head_type(&ep->host_reqs, UsbReqInternal, node);
+            req = ep->host_reqs.pop();
         }
     }
 
-    if (req_int) {
-        usb_request_t* req = INTERNAL_TO_USB_REQ(req_int);
-        usb_request_complete(req, ZX_ERR_IO_REFUSED, 0, &req_int->complete_cb);
+    if (req) {
+        req->Complete(ZX_ERR_IO_REFUSED, 0);
     }
 
     return ZX_OK;
@@ -384,24 +311,25 @@
 
 void UsbVirtualBus::UsbDciRequestQueue(usb_request_t* req,
                                        const usb_request_complete_t* complete_cb) {
-    auto* req_int = USB_REQ_TO_INTERNAL(req);
-    req_int->complete_cb = *complete_cb;
+    Request request(req, *complete_cb, sizeof(usb_request_t));
 
-    uint8_t index = EpAddressToIndex(req->header.ep_address);
+    uint8_t index = EpAddressToIndex(request.request()->header.ep_address);
     if (index == 0 || index >= USB_MAX_EPS) {
-        printf("%s: bad endpoint %u\n", __func__, req->header.ep_address);
-        usb_request_complete(req, ZX_ERR_INVALID_ARGS, 0, complete_cb);
+        printf("%s: bad endpoint %u\n", __func__, request.request()->header.ep_address);
+        request.Complete(ZX_ERR_INVALID_ARGS, 0);
         return;
     }
-    lock_.Acquire();
-    if (!connected_) {
-        lock_.Release();
-        usb_request_complete(req, ZX_ERR_IO_NOT_PRESENT, 0, complete_cb);
+    bool connected;
+    {
+        fbl::AutoLock al(&lock_);
+        connected = connected_;
+    }
+    if (!connected) {
+        request.Complete(ZX_ERR_IO_NOT_PRESENT, 0);
         return;
     }
 
-    list_add_tail(&eps_[index].device_reqs, &req_int->node);
-    lock_.Release();
+    eps_[index].device_reqs.push(std::move(request));
 
     sync_completion_signal(&thread_signal_);
 }
@@ -440,37 +368,38 @@
 }
 
 size_t UsbVirtualBus::UsbDciGetRequestSize() {
-    return sizeof(usb_request_t) + sizeof(UsbReqInternal);
+    return Request::RequestSize(sizeof(usb_request_t));
 }
 
 void UsbVirtualBus::UsbHciRequestQueue(usb_request_t* req,
                                        const usb_request_complete_t* complete_cb) {
-    auto* req_int = USB_REQ_TO_INTERNAL(req);
-    req_int->complete_cb = *complete_cb;
+    Request request(req, *complete_cb, sizeof(usb_request_t));
 
-    uint8_t index = EpAddressToIndex(req->header.ep_address);
+    uint8_t index = EpAddressToIndex(request.request()->header.ep_address);
     if (index >= USB_MAX_EPS) {
-        printf("usb_virtual_bus_host_queue bad endpoint %u\n", req->header.ep_address);
-        usb_request_complete(req, ZX_ERR_INVALID_ARGS, 0, complete_cb);
+        printf("usb_virtual_bus_host_queue bad endpoint %u\n",
+               request.request()->header.ep_address);
+        request.Complete(ZX_ERR_INVALID_ARGS, 0);
         return;
     }
 
-    lock_.Acquire();
-    if (!connected_) {
-        lock_.Release();
-        usb_request_complete(req, ZX_ERR_IO_NOT_PRESENT, 0, complete_cb);
+    bool connected;
+    {
+        fbl::AutoLock al(&lock_);
+        connected = connected_;
+    }
+    if (!connected) {
+        request.Complete(ZX_ERR_IO_NOT_PRESENT, 0);
         return;
     }
 
     usb_virtual_ep_t* ep = &eps_[index];
 
     if (ep->stalled) {
-        lock_.Release();
-        usb_request_complete(req, ZX_ERR_IO_REFUSED, 0, complete_cb);
+        request.Complete(ZX_ERR_IO_REFUSED, 0);
         return;
     }
-    list_add_tail(&ep->host_reqs, &req_int->node);
-    lock_.Release();
+    ep->host_reqs.push(std::move(request));
 
     sync_completion_signal(&thread_signal_);
 }
@@ -479,9 +408,11 @@
     if (bus_intf) {
         bus_intf_ = ddk::UsbBusInterfaceClient(bus_intf);
 
-        lock_.Acquire();
-        bool connected = connected_;
-        lock_.Release();
+        bool connected;
+        {
+            fbl::AutoLock al(&lock_);
+            connected = connected_;
+        }
 
         if (connected) {
             bus_intf_.AddDevice(DEVICE_SLOT_ID, DEVICE_HUB_ID, DEVICE_SPEED);
@@ -541,7 +472,7 @@
 }
 
 size_t UsbVirtualBus::UsbHciGetRequestSize() {
-    return sizeof(usb_request_t) + sizeof(UsbReqInternal);
+    return Request::RequestSize(sizeof(usb_request_t));
 }
 
 zx_status_t UsbVirtualBus::MsgEnable(fidl_txn_t* txn) {
diff --git a/system/dev/usb/usb-virtual-bus/usb-virtual-bus.h b/system/dev/usb/usb-virtual-bus/usb-virtual-bus.h
index d4e7399..fe8bc6a 100644
--- a/system/dev/usb/usb-virtual-bus/usb-virtual-bus.h
+++ b/system/dev/usb/usb-virtual-bus/usb-virtual-bus.h
@@ -12,6 +12,7 @@
 #include <fbl/mutex.h>
 #include <fbl/unique_ptr.h>
 #include <lib/sync/completion.h>
+#include <usb/request-cpp.h>
 
 #include <threads.h>
 
@@ -75,10 +76,13 @@
 private:
     DISALLOW_COPY_ASSIGN_AND_MOVE(UsbVirtualBus);
 
+    using Request = usb::UnownedRequest<void>;
+    using RequestQueue = usb::UnownedRequestQueue<void>;
+
     // This struct represents an endpoint on the virtual device.
     struct usb_virtual_ep_t {
-        list_node_t host_reqs __TA_GUARDED(lock_);
-        list_node_t device_reqs __TA_GUARDED(lock_);
+        RequestQueue host_reqs;
+        RequestQueue device_reqs;
         uint16_t max_packet_size = 0;
         // Offset into current host req, for dealing with host reqs that are bigger than
         // their matching device req.
@@ -91,7 +95,7 @@
     zx_status_t CreateHost();
     void SetConnected(bool connected);
     int Thread();
-    void HandleControl(usb_request_t* req);
+    void HandleControl(Request req);
     zx_status_t SetStall(uint8_t ep_address, bool stall);
 
     // Reference to class that implements the virtual device controller protocol.