fix wip

Change-Id: I96bb816209e1196ba3e93bc5c594ea751d7af3d6
diff --git a/system/dev/usb/usb-peripheral/usb-function.cpp b/system/dev/usb/usb-peripheral/usb-function.cpp
index d26df9c..58bb517 100644
--- a/system/dev/usb/usb-peripheral/usb-function.cpp
+++ b/system/dev/usb/usb-peripheral/usb-function.cpp
@@ -6,11 +6,13 @@
 #include "usb-peripheral.h"
 
 #include <ddk/debug.h>
+#include <fbl/array.h>
 
 namespace usb_peripheral {
 
 void UsbFunction::DdkRelease() {
-    // Do nothing here. This object is owned by the UsbPeripheral class so we don't delete ourself.
+    // Release the reference how that devmgr no longer has a pointer to the function.
+    __UNUSED bool dummy = Release();
 }
 
 // UsbFunctionProtocol implementation.
@@ -32,30 +34,31 @@
     function_intf_.GetDescriptors(descriptors, length, &actual);
     if (actual != length) {
         zxlogf(ERROR, "UsbFunctionInterfaceClient::GetDescriptors() failed\n");
+        delete[] descriptors;
         return ZX_ERR_INTERNAL;
     }
 
-    auto status = peripheral_->ValidateFunction(this, descriptors, length, &num_interfaces_);
+    auto status = peripheral_->ValidateFunction(fbl::RefPtr<UsbFunction>(this), descriptors, length,
+                                                &num_interfaces_);
     if (status != ZX_OK) {
+        delete[] descriptors;
         return status;
     }
 
-    descriptors_ = static_cast<usb_descriptor_header_t*>(malloc(length));
-    if (!descriptors_) {
+    descriptors_.reset(descriptors, length);
+    if (!ac.check()) {
         return ZX_ERR_NO_MEMORY;
     }
-    memcpy(descriptors_, descriptors, length);
-    descriptors_length_ = length;
 
     return peripheral_->FunctionRegistered();
 }
 
 zx_status_t UsbFunction::UsbFunctionAllocInterface(uint8_t* out_intf_num) {
-    return peripheral_->AllocInterface(this, out_intf_num);
+    return peripheral_->AllocInterface(fbl::RefPtr<UsbFunction>(this), out_intf_num);
 }
 
 zx_status_t UsbFunction::UsbFunctionAllocEp(uint8_t direction, uint8_t* out_address) {
-    return peripheral_->AllocEndpoint(this, direction, out_address);
+    return peripheral_->AllocEndpoint(fbl::RefPtr<UsbFunction>(this), direction, out_address);
 }
 
 zx_status_t UsbFunction::UsbFunctionConfigEp(const usb_endpoint_descriptor_t* ep_desc,
@@ -67,8 +70,8 @@
     return peripheral_->dci().DisableEp(address);
 }
 
-zx_status_t UsbFunction::UsbFunctionAllocStringDesc(const char* string, uint8_t* out_index) {
-    return peripheral_->AllocStringDesc(string, out_index);
+zx_status_t UsbFunction::UsbFunctionAllocStringDesc(const char* str, uint8_t* out_index) {
+    return peripheral_->AllocStringDesc(str, out_index);
 }
 
 void UsbFunction::UsbFunctionRequestQueue(usb_request_t* usb_request,
diff --git a/system/dev/usb/usb-peripheral/usb-function.h b/system/dev/usb/usb-peripheral/usb-function.h
index 72738e1..4d777c2 100644
--- a/system/dev/usb/usb-peripheral/usb-function.h
+++ b/system/dev/usb/usb-peripheral/usb-function.h
@@ -7,6 +7,8 @@
 #include <ddktl/device.h>
 #include <ddktl/protocol/usb/dci.h>
 #include <ddktl/protocol/usb/function.h>
+#include <fbl/array.h>
+#include <fbl/ref_counted.h>
 #include <fuchsia/hardware/usb/peripheral/c/fidl.h>
 
 #include "usb-peripheral.h"
@@ -19,15 +21,12 @@
 // This class represents a USB function in the peripheral role configurations.
 // USB function drivers bind to this.
 class UsbFunction : public UsbFunctionType,
-                    public ddk::UsbFunctionProtocol<UsbFunction, ddk::base_protocol> {
+                    public ddk::UsbFunctionProtocol<UsbFunction, ddk::base_protocol>,
+                    public fbl::RefCounted<UsbFunction>  {
 public:
     UsbFunction(zx_device_t* parent, UsbPeripheral* peripheral, const FunctionDescriptor* desc)
         : UsbFunctionType(parent), peripheral_(peripheral), function_descriptor_(*desc) {}
 
-    ~UsbFunction() {
-        delete[] descriptors_;
-    }
-
     // Device protocol implementation.
     void DdkRelease();
 
@@ -38,7 +37,7 @@
     zx_status_t UsbFunctionConfigEp(const usb_endpoint_descriptor_t* ep_desc,
                                     const usb_ss_ep_comp_descriptor_t* ss_comp_desc);
     zx_status_t UsbFunctionDisableEp(uint8_t address);
-    zx_status_t UsbFunctionAllocStringDesc(const char* string, uint8_t* out_index);
+    zx_status_t UsbFunctionAllocStringDesc(const char* str, uint8_t* out_index);
     void UsbFunctionRequestQueue(usb_request_t* usb_request,
                                  const usb_request_complete_t* complete_cb);
     zx_status_t UsbFunctionEpSetStall(uint8_t ep_address);
@@ -51,8 +50,8 @@
                         void* read_buffer, size_t read_size, size_t* out_read_actual);
 
     inline const usb_descriptor_header_t* GetDescriptors(size_t* out_length) const {
-        *out_length = descriptors_length_;
-        return descriptors_;
+        *out_length = descriptors_.size();
+        return reinterpret_cast<usb_descriptor_header_t*>(descriptors_.get());
     }
 
     inline const FunctionDescriptor& GetFunctionDescriptor() const { return function_descriptor_; }
@@ -69,8 +68,7 @@
     const FunctionDescriptor function_descriptor_;
 
     uint8_t num_interfaces_ = 0;
-    usb_descriptor_header_t* descriptors_ = nullptr;
-    size_t descriptors_length_ = 0;
+    fbl::Array<uint8_t> descriptors_;
 };
 
  } // namespace usb_peripheral
diff --git a/system/dev/usb/usb-peripheral/usb-peripheral.cpp b/system/dev/usb/usb-peripheral/usb-peripheral.cpp
index 1aa4e5a..7b2f9cc 100644
--- a/system/dev/usb/usb-peripheral/usb-peripheral.cpp
+++ b/system/dev/usb/usb-peripheral/usb-peripheral.cpp
@@ -21,6 +21,7 @@
 #include <ddk/protocol/usb/modeswitch.h>
 #include <fbl/alloc_checker.h>
 #include <fbl/auto_lock.h>
+#include <fbl/unique_ptr.h>
 #include <zircon/device/usb-peripheral.h>
 #include <zircon/hw/usb.h>
 #include <zircon/hw/usb/cdc.h>
@@ -30,20 +31,20 @@
 
 namespace usb_peripheral {
 
-zx_status_t UsbPeripheral::Create(zx_device_t* parent) {
+zx_status_t UsbPeripheral::Create(void* ctx, zx_device_t* parent) {
     fbl::AllocChecker ac;
-    auto bus = fbl::make_unique_checked<UsbPeripheral>(&ac, parent);
+    auto device = fbl::make_unique_checked<UsbPeripheral>(&ac, parent);
     if (!ac.check()) {
         return ZX_ERR_NO_MEMORY;
     }
 
-    auto status = bus->Init();
+    auto status = device->Init();
     if (status != ZX_OK) {
         return status;
     }
 
     // devmgr is now in charge of the device.
-    __UNUSED auto* dummy = bus.release();
+    __UNUSED auto* dummy = device.release();
     return ZX_OK;
 }
 
@@ -72,8 +73,7 @@
     }
     parent_request_size_ = dci_.GetRequestSize();
 
-    status = DdkAdd("usb-peripheral", DEVICE_ADD_NON_BINDABLE, nullptr, 0,
-                    ZX_PROTOCOL_USB_PERIPHERAL);
+    status = DdkAdd("usb-peripheral", DEVICE_ADD_NON_BINDABLE);
     if (status != ZX_OK) {
         return status;
     }
@@ -106,8 +106,8 @@
     return ZX_OK;
 }
 
-zx_status_t UsbPeripheral::ValidateFunction(UsbFunction* function, void* descriptors, size_t length,
-                                            uint8_t* out_num_interfaces) {
+zx_status_t UsbPeripheral::ValidateFunction(fbl::RefPtr<UsbFunction> function, void* descriptors,
+                                            size_t length, uint8_t* out_num_interfaces) {
     auto* intf_desc = static_cast<usb_interface_descriptor_t*>(descriptors);
     if (intf_desc->bDescriptorType != USB_DT_INTERFACE ||
             intf_desc->bLength != sizeof(usb_interface_descriptor_t)) {
@@ -159,7 +159,7 @@
 zx_status_t UsbPeripheral::FunctionRegistered() {
     fbl::AutoLock lock(&lock_);
 
-    if (config_desc_) {
+    if (config_desc_.size() == 0) {
         zxlogf(ERROR, "usb_device_function_registered: already have configuration descriptor!\n");
         return ZX_ERR_BAD_STATE;
     }
@@ -180,11 +180,11 @@
 
     // build our configuration descriptor
     fbl::AllocChecker ac;
-    auto* config_desc = reinterpret_cast<usb_configuration_descriptor_t*>(
-                                                                    new (&ac) uint8_t[length]);
+    auto* config_desc_bytes = new (&ac) uint8_t[length];
     if (!ac.check()) {
         return ZX_ERR_NO_MEMORY;
     }
+    auto* config_desc = reinterpret_cast<usb_configuration_descriptor_t*>(config_desc_bytes);
 
     config_desc->bLength = sizeof(*config_desc);
     config_desc->bDescriptorType = USB_DT_CONFIG;
@@ -206,7 +206,7 @@
         config_desc->bNumInterfaces = static_cast<uint8_t>(config_desc->bNumInterfaces +
                                                            function->GetNumInterfaces());
     }
-    config_desc_ = config_desc;
+    config_desc_.reset(config_desc_bytes, length);
 
     zxlogf(TRACE, "usb_device_function_registered functions_registered = true\n");
     functions_registered_ = true;
@@ -214,7 +214,8 @@
     return DeviceStateChanged();
 }
 
-zx_status_t UsbPeripheral::AllocInterface(UsbFunction* function, uint8_t* out_intf_num) {
+zx_status_t UsbPeripheral::AllocInterface(fbl::RefPtr<UsbFunction> function,
+                                          uint8_t* out_intf_num) {
     fbl::AutoLock lock(&lock_);
 
     for (uint8_t i = 0; i < countof(interface_map_); i++) {
@@ -228,7 +229,7 @@
     return ZX_ERR_NO_RESOURCES;
 }
 
-zx_status_t UsbPeripheral::AllocEndpoint(UsbFunction* function, uint8_t direction,
+zx_status_t UsbPeripheral::AllocEndpoint(fbl::RefPtr<UsbFunction> function, uint8_t direction,
                                          uint8_t* out_address) {
     uint8_t start, end;
 
@@ -258,64 +259,68 @@
                                          void* buffer, size_t length, size_t* out_actual) {
     uint8_t type = request_type & USB_TYPE_MASK;
 
-    if (type == USB_TYPE_STANDARD) {
-        auto desc_type = static_cast<uint8_t>(value >> 8);
-        if (desc_type == USB_DT_DEVICE && index == 0) {
-            if (device_desc_.bLength == 0) {
-                zxlogf(ERROR, "%s: device descriptor not set\n", __func__);
-                return ZX_ERR_INTERNAL;
-            }
-            if (length > sizeof(device_desc_)) length = sizeof(device_desc_);
-            memcpy(buffer, &device_desc_, length);
-            *out_actual = length;
-            return ZX_OK;
-        } else if (desc_type == USB_DT_CONFIG && index == 0) {
-            const usb_configuration_descriptor_t* desc = config_desc_;
-            if (!desc) {
-                zxlogf(ERROR, "%s: configuration descriptor not set\n", __func__);
-                return ZX_ERR_INTERNAL;
-            }
-            uint16_t desc_length = letoh16(desc->wTotalLength);
-            if (length > desc_length) length =desc_length;
-            memcpy(buffer, desc, length);
-            *out_actual = length;
-            return ZX_OK;
+    if (type != USB_TYPE_STANDARD) {
+        zxlogf(ERROR, "%s unsupported value: %d index: %d\n", __func__, value, index);
+        return ZX_ERR_NOT_SUPPORTED;
+    }
+
+    auto desc_type = static_cast<uint8_t>(value >> 8);
+    if (desc_type == USB_DT_DEVICE && index == 0) {
+        if (device_desc_.bLength == 0) {
+            zxlogf(ERROR, "%s: device descriptor not set\n", __func__);
+            return ZX_ERR_INTERNAL;
         }
-        else if (value >> 8 == USB_DT_STRING) {
-            uint8_t desc[255];
-            auto* header = reinterpret_cast<usb_descriptor_header_t*>(desc);
-            header->bDescriptorType = USB_DT_STRING;
-
-            auto string_index = static_cast<uint8_t>(value & 0xFF);
-            if (string_index == 0) {
-                // special case - return language list
-                header->bLength = 4;
-                desc[2] = 0x09;     // language ID
-                desc[3] = 0x04;
-            } else {
-                // String indices are 1-based.
-                string_index--;
-                if (string_index >= strings_.size()) {
-                    return ZX_ERR_INVALID_ARGS;
-                }
-                const char* string = strings_[string_index].c_str();
-                unsigned index = 2;
-
-                // convert ASCII to Unicode
-                if (string) {
-                    while (*string && index < sizeof(desc) - 2) {
-                        desc[index++] = *string++;
-                        desc[index++] = 0;
-                    }
-                }
-                header->bLength = static_cast<uint8_t>(index);
-            }
-
-            if (header->bLength < length) length = header->bLength;
-            memcpy(buffer, desc, length);
-            *out_actual = length;
-            return ZX_OK;
+        if (length > sizeof(device_desc_)) length = sizeof(device_desc_);
+        memcpy(buffer, &device_desc_, length);
+        *out_actual = length;
+        return ZX_OK;
+    } else if (desc_type == USB_DT_CONFIG && index == 0) {
+        if (config_desc_.size() == 0) {
+            zxlogf(ERROR, "%s: configuration descriptor not set\n", __func__);
+            return ZX_ERR_INTERNAL;
         }
+        auto desc_length = config_desc_.size();
+        if (length > desc_length) {
+            length = desc_length;
+        }
+        memcpy(buffer, config_desc_.get(), length);
+        *out_actual = length;
+        return ZX_OK;
+    }
+    else if (value >> 8 == USB_DT_STRING) {
+        uint8_t desc[255];
+        auto* header = reinterpret_cast<usb_descriptor_header_t*>(desc);
+        header->bDescriptorType = USB_DT_STRING;
+
+        auto string_index = static_cast<uint8_t>(value & 0xFF);
+        if (string_index == 0) {
+            // special case - return language list
+            header->bLength = 4;
+            desc[2] = 0x09;     // language ID
+            desc[3] = 0x04;
+        } else {
+            // String indices are 1-based.
+            string_index--;
+            if (string_index >= strings_.size()) {
+                return ZX_ERR_INVALID_ARGS;
+            }
+            const char* string = strings_[string_index].c_str();
+            unsigned index = 2;
+
+            // convert ASCII to UTF16
+            if (string) {
+                while (*string && index < sizeof(desc) - 2) {
+                    desc[index++] = *string++;
+                    desc[index++] = 0;
+                }
+            }
+            header->bLength = static_cast<uint8_t>(index);
+        }
+
+        if (header->bLength < length) length = header->bLength;
+        memcpy(buffer, desc, length);
+        *out_actual = length;
+        return ZX_OK;
     }
 
     zxlogf(ERROR, "%s unsupported value: %d index: %d\n", __func__, value, index);
@@ -345,8 +350,8 @@
         return ZX_ERR_OUT_OF_RANGE;
     }
 
-    auto* function = interface_map_[interface];
-    if (function) {
+    auto function = interface_map_[interface];
+    if (function != nullptr) {
         return function->SetInterface(interface, alt_setting);
     }
     return ZX_ERR_NOT_SUPPORTED;
@@ -358,12 +363,12 @@
     }
 
     fbl::AllocChecker ac;
-    auto function = fbl::make_unique_checked<UsbFunction>(&ac, zxdev(), this, desc);
+    auto function = fbl::MakeRefCountedChecked<UsbFunction>(&ac, zxdev(), this, desc);
     if (!ac.check()) {
         return ZX_ERR_NO_MEMORY;
     }
 
-    functions_.push_back(std::move(function));
+    functions_.push_back(function);
 
     return ZX_OK;
 }
@@ -400,14 +405,16 @@
         }
     }
     functions_.reset();
-
-    delete[] config_desc_;
-    config_desc_ = nullptr;
+    config_desc_.reset();
     functions_bound_ = false;
     functions_registered_ = false;
 
-    memset(interface_map_, 0, sizeof(interface_map_));
-    memset(endpoint_map_, 0, sizeof(endpoint_map_));
+    for (size_t i = 0; i < countof(interface_map_); i++) {
+        interface_map_[i].reset();
+    }
+    for (size_t i = 0; i < countof(endpoint_map_); i++) {
+        endpoint_map_[i].reset();
+    }
     strings_.reset();
 
     return DeviceStateChanged();
@@ -420,7 +427,7 @@
     }
 
     for (unsigned i = 0; i < functions_.size(); i++) {
-        auto* function = functions_[i].get();
+        auto function = functions_[i];
         char name[16];
         snprintf(name, sizeof(name), "function-%03u", i);
 
@@ -440,6 +447,8 @@
             zxlogf(ERROR, "usb_dev_bind_functions add_device failed %d\n", status);
             return status;
         }
+        // Hold a reference while devmgr has a pointer to the function.
+        function->AddRef();
     }
 
     function_devs_added_ = true;
@@ -456,8 +465,7 @@
         function->DdkRemove();
     }
 
-    delete[] config_desc_;
-    config_desc_ = NULL;
+    config_desc_.reset();
     functions_registered_ = false;
     function_devs_added_ = false;
 }
@@ -557,8 +565,8 @@
                 return ZX_ERR_OUT_OF_RANGE;
             }
             // delegate to the function driver for the interface
-            auto* function = interface_map_[index];
-            if (function) {
+            auto function = interface_map_[index];
+            if (function != nullptr) {
                 return function->Control(setup, write_buffer, write_size, read_buffer, read_size,
                                          out_read_actual);
             }
@@ -574,8 +582,8 @@
         if (index >= countof(endpoint_map_)) {
             return ZX_ERR_OUT_OF_RANGE;
         }
-        auto* function = endpoint_map_[index];
-        if (function ) {
+        auto function = endpoint_map_[index];
+        if (function != nullptr) {
             return function->Control(setup, write_buffer, write_size, read_buffer, read_size,
                                      out_read_actual);
         }
@@ -703,7 +711,6 @@
 
 void UsbPeripheral::DdkRelease() {
     zxlogf(TRACE, "%s\n", __func__);
-    delete[] config_desc_;
     delete this;
 }
 
@@ -764,14 +771,10 @@
 }
 #endif // defined(USB_DEVICE_VID) && defined(USB_DEVICE_PID) && defined(USB_DEVICE_FUNCTIONS)
 
-zx_status_t usb_peripheral_bind(void* ctx, zx_device_t* parent) {
-    return UsbPeripheral::Create(parent);
-}
-
 static zx_driver_ops_t ops = [](){
     zx_driver_ops_t ops = {};
     ops.version = DRIVER_OPS_VERSION;
-    ops.bind = usb_peripheral_bind;
+    ops.bind = UsbPeripheral::Create;
     return ops;
 }();
 
diff --git a/system/dev/usb/usb-peripheral/usb-peripheral.h b/system/dev/usb/usb-peripheral/usb-peripheral.h
index 5ffa2d5..cc72f08 100644
--- a/system/dev/usb/usb-peripheral/usb-peripheral.h
+++ b/system/dev/usb/usb-peripheral/usb-peripheral.h
@@ -5,12 +5,14 @@
 #pragma once
 
 #include <ddktl/device.h>
+#include <ddktl/protocol/empty-protocol.h>
 #include <ddktl/protocol/usb/dci.h>
 #include <ddktl/protocol/usb/function.h>
 #include <ddktl/protocol/usb/modeswitch.h>
+#include <fbl/array.h>
 #include <fbl/mutex.h>
+#include <fbl/ref_ptr.h>
 #include <fbl/string.h>
-#include <fbl/unique_ptr.h>
 #include <fbl/vector.h>
 #include <fuchsia/hardware/usb/peripheral/c/fidl.h>
 
@@ -75,12 +77,14 @@
 // This is the main class for the USB peripheral role driver.
 // It binds against the USB DCI driver device and manages a list of UsbFunction devices,
 // one for each USB function in the peripheral role configuration.
-class UsbPeripheral : public UsbPeripheralType, public ddk::UsbDciInterface<UsbPeripheral> {
+class UsbPeripheral : public UsbPeripheralType,
+                      public ddk::EmptyProtocol<ZX_PROTOCOL_USB_PERIPHERAL>,
+                      public ddk::UsbDciInterface<UsbPeripheral> {
 public:
     UsbPeripheral(zx_device_t* parent)
         : UsbPeripheralType(parent), dci_(parent), ums_(parent) {}
 
-    static zx_status_t Create(zx_device_t* parent);
+    static zx_status_t Create(void* ctx, zx_device_t* parent);
 
     // Device protocol implementation.
     zx_status_t DdkMessage(fidl_msg_t* msg, fidl_txn_t* txn);
@@ -103,12 +107,13 @@
     zx_status_t MsgGetMode(fidl_txn_t* txn);
     zx_status_t MsgSetMode(uint32_t mode, fidl_txn_t* txn);
 
-    zx_status_t SetFunctionInterface(UsbFunction* function,
+    zx_status_t SetFunctionInterface(fbl::RefPtr<UsbFunction> function,
                                      const usb_function_interface_t* interface);
-    zx_status_t AllocInterface(UsbFunction* function, uint8_t* out_intf_num);
-    zx_status_t AllocEndpoint(UsbFunction* function, uint8_t direction, uint8_t* out_address);
+    zx_status_t AllocInterface(fbl::RefPtr<UsbFunction> function, uint8_t* out_intf_num);
+    zx_status_t AllocEndpoint(fbl::RefPtr<UsbFunction> function, uint8_t direction,
+                              uint8_t* out_address);
     zx_status_t AllocStringDesc(const char* string, uint8_t* out_index);
-    zx_status_t ValidateFunction(UsbFunction* function, void* descriptors, size_t length,
+    zx_status_t ValidateFunction(fbl::RefPtr<UsbFunction> function, void* descriptors, size_t length,
                                  uint8_t* out_num_interfaces);
     zx_status_t FunctionRegistered();
 
@@ -159,21 +164,21 @@
     // USB device descriptor set via ioctl_usb_peripheral_set_device_desc()
     usb_device_descriptor_t device_desc_ = {};
     // USB configuration descriptor, synthesized from our functions' descriptors.
-    usb_configuration_descriptor_t* config_desc_ = nullptr;
+    fbl::Array<uint8_t> config_desc_;
     // Map from interface number to function.
-    UsbFunction* interface_map_[MAX_INTERFACES] = {};
+    fbl::RefPtr<UsbFunction> interface_map_[MAX_INTERFACES];
     // Map from endpoint index to function.
-    UsbFunction* endpoint_map_[USB_MAX_EPS] = {};
+    fbl::RefPtr<UsbFunction> endpoint_map_[USB_MAX_EPS];
     // Strings for USB string descriptors.
     fbl::Vector<fbl::String> strings_;
     // List of usb_function_t.
-    fbl::Vector<fbl::unique_ptr<UsbFunction>> functions_;
+    fbl::Vector<fbl::RefPtr<UsbFunction>> functions_;
     // mutex for protecting our state
     fbl::Mutex lock_;
     // Current USB mode set via ioctl_usb_peripheral_set_mode()
     usb_mode_t usb_mode_ = USB_MODE_NONE;
     // Our parent's USB mode.
-     usb_mode_t dci_usb_mode_ = USB_MODE_NONE;
+    usb_mode_t dci_usb_mode_ = USB_MODE_NONE;
     // Set if ioctl_usb_peripheral_bind_functions() has been called
     // and we have a complete list of our function.
     bool functions_bound_ = false;