| // Copyright 2017 The Fuchsia Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include <dirent.h> |
| #include <fcntl.h> |
| #include <new> |
| #include <stdio.h> |
| #include <stdlib.h> |
| #include <string.h> |
| #include <unistd.h> |
| |
| #include "../shared/env.h" |
| #include "../shared/fdio.h" |
| #include "../shared/log.h" |
| #include "driver.h" |
| |
| #include <driver-info/driver-info.h> |
| |
| #include <zircon/driver/binding.h> |
| |
| namespace { |
| |
| struct AddContext { |
| const char* libname; |
| devmgr::DriverLoadCallback func; |
| }; |
| |
| bool is_driver_disabled(const char* name) { |
| // driver.<driver_name>.disable |
| char opt[16 + DRIVER_NAME_LEN_MAX]; |
| snprintf(opt, 16 + DRIVER_NAME_LEN_MAX, "driver.%s.disable", name); |
| return devmgr::getenv_bool(opt, false); |
| } |
| |
| void found_driver(zircon_driver_note_payload_t* note, const zx_bind_inst_t* bi, void* cookie) { |
| auto context = static_cast<const AddContext*>(cookie); |
| |
| // ensure strings are terminated |
| note->name[sizeof(note->name) - 1] = 0; |
| note->vendor[sizeof(note->vendor) - 1] = 0; |
| note->version[sizeof(note->version) - 1] = 0; |
| |
| if (is_driver_disabled(note->name)) { |
| return; |
| } |
| |
| auto drv = fbl::make_unique<devmgr::Driver>(); |
| if (drv == nullptr) { |
| return; |
| } |
| |
| auto binding = fbl::make_unique<zx_bind_inst_t[]>(note->bindcount); |
| if (binding == nullptr) { |
| return; |
| } |
| const size_t bindlen = note->bindcount * sizeof(zx_bind_inst_t); |
| memcpy(binding.get(), bi, bindlen); |
| drv->binding.reset(binding.release()); |
| drv->binding_size = static_cast<uint32_t>(bindlen); |
| |
| drv->flags = note->flags; |
| drv->libname.Set(context->libname); |
| drv->name.Set(note->name); |
| |
| #if VERBOSE_DRIVER_LOAD |
| printf("found driver: %s\n", (char*)cookie); |
| printf(" name: %s\n", note->name); |
| printf(" vendor: %s\n", note->vendor); |
| printf(" version: %s\n", note->version); |
| printf(" flags: %#x\n", note->flags); |
| printf(" binding:\n"); |
| for (size_t n = 0; n < note->bindcount; n++) { |
| printf(" %03zd: %08x %08x\n", n, bi[n].op, bi[n].arg); |
| } |
| #endif |
| |
| context->func(drv.release(), note->version); |
| } |
| |
| } // namespace |
| |
| namespace devmgr { |
| |
| void find_loadable_drivers(const char* path, DriverLoadCallback func) { |
| |
| DIR* dir = opendir(path); |
| if (dir == nullptr) { |
| return; |
| } |
| AddContext context = {"", std::move(func)}; |
| |
| struct dirent* de; |
| while ((de = readdir(dir)) != nullptr) { |
| if (de->d_name[0] == '.') { |
| continue; |
| } |
| if (de->d_type != DT_REG) { |
| continue; |
| } |
| char libname[256 + 32]; |
| int r = snprintf(libname, sizeof(libname), "%s/%s", path, de->d_name); |
| if ((r < 0) || (r >= (int)sizeof(libname))) { |
| continue; |
| } |
| context.libname = libname; |
| |
| int fd; |
| if ((fd = openat(dirfd(dir), de->d_name, O_RDONLY)) < 0) { |
| continue; |
| } |
| zx_status_t status = di_read_driver_info(fd, &context, found_driver); |
| close(fd); |
| |
| if (status) { |
| if (status == ZX_ERR_NOT_FOUND) { |
| printf("devcoord: no driver info in '%s'\n", libname); |
| } else { |
| printf("devcoord: error reading info from '%s'\n", libname); |
| } |
| } |
| } |
| closedir(dir); |
| } |
| |
| void load_driver(const char* path, DriverLoadCallback func) { |
| // TODO: check for duplicate driver add |
| int fd; |
| if ((fd = open(path, O_RDONLY)) < 0) { |
| printf("devcoord: cannot open '%s'\n", path); |
| return; |
| } |
| |
| AddContext context = {path, std::move(func)}; |
| zx_status_t status = di_read_driver_info(fd, &context, found_driver); |
| close(fd); |
| |
| if (status) { |
| if (status == ZX_ERR_NOT_FOUND) { |
| printf("devcoord: no driver info in '%s'\n", path); |
| } else { |
| printf("devcoord: error reading info from '%s'\n", path); |
| } |
| } |
| } |
| |
| } // namespace devmgr |