1// Copyright 2018 The Fuchsia Authors. All rights reserved. 2// Use of this source code is governed by a BSD-style license that can be 3// found in the LICENSE file. 4 5#include <fbl/auto_call.h> 6#include <fbl/unique_fd.h> 7#include <fbl/unique_ptr.h> 8#include <lib/fdio/watcher.h> 9#include <lib/zx/vmo.h> 10#include <zircon/device/usb-device.h> 11#include <zircon/device/usb-test-fwloader.h> 12#include <zircon/device/usb-tester.h> 13#include <zircon/hw/usb.h> 14#include <zircon/types.h> 15 16#include <dirent.h> 17#include <errno.h> 18#include <fcntl.h> 19#include <stdio.h> 20#include <string.h> 21#include <sys/stat.h> 22 23static const char* const DEV_FX3_DIR = "/dev/class/usb-test-fwloader"; 24static const char* const DEV_USB_TESTER_DIR = "/dev/class/usb-tester"; 25 26static const int ENUMERATION_WAIT_SECS = 5; 27 28static constexpr uint32_t BUFFER_SIZE = 8 * 1024; 29 30static inline int MSB(int n) { return n >> 8; } 31static inline int LSB(int n) { return n & 0xFF; } 32 33zx_status_t watch_dir_cb(int dirfd, int event, const char* filename, void* cookie) { 34 if (event != WATCH_EVENT_ADD_FILE) { 35 return ZX_OK; 36 } 37 int fd = openat(dirfd, filename, O_RDWR); 38 if (fd < 0) { 39 return ZX_OK; 40 } 41 auto out_fd = reinterpret_cast<int*>(cookie); 42 *out_fd = fd; 43 return ZX_ERR_STOP; 44} 45 46// Waits for a device to enumerate and be added to the given directory. 47zx_status_t wait_dev_enumerate(const char* dir, fbl::unique_fd& out_fd) { 48 DIR* d = opendir(dir); 49 if (d == nullptr) { 50 fprintf(stderr, "Could not open dir: \"%s\"\n", dir); 51 return ZX_ERR_BAD_STATE; 52 } 53 auto close_dir = fbl::MakeAutoCall([&] { closedir(d); }); 54 int fd = 0; 55 zx_status_t status = fdio_watch_directory(dirfd(d), watch_dir_cb, 56 zx_deadline_after(ZX_SEC(ENUMERATION_WAIT_SECS)), 57 reinterpret_cast<void*>(&fd)); 58 if (status == ZX_ERR_STOP) { 59 out_fd.reset(fd); 60 return ZX_OK; 61 } else { 62 return status; 63 } 64} 65 66zx_status_t open_dev(const char* dir, fbl::unique_fd& out_fd) { 67 DIR* d = opendir(dir); 68 if (d == nullptr) { 69 fprintf(stderr, "Could not open dir: \"%s\"\n", dir); 70 return ZX_ERR_BAD_STATE; 71 } 72 73 struct dirent* de; 74 while ((de = readdir(d)) != nullptr) { 75 int fd = openat(dirfd(d), de->d_name, O_RDWR); 76 if (fd < 0) { 77 continue; 78 } 79 out_fd.reset(fd); 80 closedir(d); 81 return ZX_OK; 82 } 83 closedir(d); 84 85 return ZX_ERR_NOT_FOUND; 86} 87 88zx_status_t open_fwloader_dev(fbl::unique_fd& out_fd) { 89 return open_dev(DEV_FX3_DIR, out_fd); 90} 91 92zx_status_t open_usb_tester_dev(fbl::unique_fd& out_fd) { 93 return open_dev(DEV_USB_TESTER_DIR, out_fd); 94} 95// Reads the firmware file and populates the provided vmo with the contents. 96static zx_status_t read_firmware(fbl::unique_fd& file_fd, zx::vmo& vmo) { 97 struct stat s; 98 if (fstat(file_fd.get(), &s) < 0) { 99 fprintf(stderr, "could not get size of file, err: %s\n", strerror(errno)); 100 return ZX_ERR_IO; 101 } 102 zx_status_t status = zx::vmo::create(s.st_size, 0, &vmo); 103 if (status != ZX_OK) { 104 return status; 105 } 106 107 fbl::unique_ptr<unsigned char[]> buf(new unsigned char[BUFFER_SIZE]); 108 ssize_t res; 109 off_t total_read = 0; 110 while ((total_read < s.st_size) && 111 ((res = read(file_fd.get(), buf.get(), BUFFER_SIZE)) != 0)) { 112 if (res < 0) { 113 fprintf(stderr, "Fatal read error: %s\n", strerror(errno)); 114 return ZX_ERR_IO; 115 } 116 zx_status_t status = vmo.write(buf.get(), total_read, res); 117 if (status != ZX_OK) { 118 return status; 119 } 120 total_read += res; 121 } 122 if (total_read != s.st_size) { 123 fprintf(stderr, "Read %jd bytes, want %jd\n", (intmax_t)total_read, (intmax_t)s.st_size); 124 return ZX_ERR_IO; 125 } 126 return ZX_OK; 127} 128 129int main(int argc, char** argv) { 130 if (argc != 2) { 131 printf("Usage: %s <firmware_image_path>\n", argv[0]); 132 return -1; 133 } 134 const char* filename = argv[1]; 135 fbl::unique_fd file_fd(open(filename, O_RDONLY)); 136 if (!file_fd) { 137 fprintf(stderr, "Failed to open \"%s\", err: %s\n", filename, strerror(errno)); 138 return -1; 139 } 140 zx::vmo fw_vmo; 141 zx_status_t status = read_firmware(file_fd, fw_vmo); 142 if (status != ZX_OK) { 143 fprintf(stderr, "Failed to read firmware file, err: %d\n", status); 144 return -1; 145 } 146 fbl::unique_fd fd; 147 status = open_fwloader_dev(fd); 148 if (status != ZX_OK) { 149 // Check if there is a usb tester device we can switch to firmware loading mode. 150 status = open_usb_tester_dev(fd); 151 if (status != ZX_OK) { 152 fprintf(stderr, "No usb test fwloader or tester device found, err: %d\n", status); 153 return -1; 154 } 155 printf("Switching usb tester device to fwloader mode\n"); 156 ssize_t res = ioctl_usb_tester_set_mode_fwloader(fd.get()); 157 if (res != 0) { 158 fprintf(stderr, "Failed to switch usb test device to fwloader mode, err: %zd\n", res); 159 return -1; 160 } 161 status = wait_dev_enumerate(DEV_FX3_DIR, fd); 162 if (status != ZX_OK) { 163 fprintf(stderr, "Failed to wait for fwloader to re-enumerate, err: %d\n", status); 164 return -1; 165 } 166 } 167 zx_handle_t handle = fw_vmo.release(); 168 ssize_t res = ioctl_usb_test_fwloader_load_firmware(fd.get(), &handle); 169 if (res < ZX_OK) { 170 fprintf(stderr, "Failed to load firmware, err: %zd\n", res); 171 return -1; 172 } 173 status = wait_dev_enumerate(DEV_USB_TESTER_DIR, fd); 174 if (status != ZX_OK) { 175 fprintf(stderr, "Failed to wait for updated usb tester to enumerate, err: %d\n", status); 176 return -1; 177 } 178 usb_device_descriptor_t device_desc; 179 res = ioctl_usb_get_device_desc(fd.get(), &device_desc); 180 if (res != sizeof(device_desc)) { 181 printf("Failed to get updated usb tester device descriptor, err: %zd\n", res); 182 return -1; 183 } 184 printf("Updated usb tester firmware to v%x.%x\n", 185 MSB(device_desc.bcdDevice), LSB(device_desc.bcdDevice)); 186 return 0; 187} 188