blob: 55f5dbb725463d9300341a742765176e187c11c8 [file] [log] [blame]
// Copyright 2017 The Chromium OS 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 "cfm-device-monitor/camera-monitor/tools.h"
#include <poll.h>
#include <unistd.h>
#include <fstream>
#include <iostream>
#include <sstream>
#include <string>
#include "base/files/file_util.h"
#include "base/logging.h"
#include "base/threading/platform_thread.h"
namespace huddly_monitor {
namespace {
const base::TimeDelta kHotplugSleepTime =
base::Milliseconds(200);
const uint8_t kBufLength = 100;
} // namespace
void ConsumeAndDiscardInput(FILE *stream) {
char buffer[kBufLength];
int fd = fileno(stream);
base::TimeTicks start_time = base::TimeTicks::Now();
while (base::TimeTicks::Now() - start_time < kHotplugSleepTime) {
struct pollfd fds = { fd, POLLIN, 0 };
if (poll(&fds, 1, 100) && (fds.revents & POLLIN)) {
HANDLE_EINTR(read(fd, buffer, kBufLength));
}
}
}
bool LookForErrorBlocking(const std::string &error_key,
const std::string &error_exception,
FILE *pipe,
std::string *err_msg) {
char buffer[kBufLength];
std::string logs = "";
if (!pipe) {
*err_msg = "Opening pipe failed";
return false;
}
int fd = fileno(pipe);
while (true) {
memset(buffer, 0, kBufLength);
// Use read so that fetched lines are not buffered. Newlines will not be
// handled properly after a match but this should be OK as the rest of the
// stream is consumed and discarded when this happens.
ssize_t read_result = HANDLE_EINTR(read(fd, buffer, kBufLength));
if (read_result > 0)
logs.append(buffer, 0, read_result);
else
break;
if (logs.rfind(error_key) != std::string::npos) {
// TODO(felixe): Remove risk of matching |error_key| but not
// |error_exception| if read() breaks the line at an unlucky place.
return error_exception.empty() ||
logs.rfind(error_exception) == std::string::npos;
}
}
return false;
}
uint32_t GetGpioNumGuado(uint8_t bus_num, uint8_t port_num) {
const uint32_t kFrontLeft = 218;
const uint32_t kFrontRight = 219;
const uint32_t kBackDual = 209;
if (bus_num == 1) {
switch (port_num) {
case 2:
return kFrontLeft;
case 3:
return kFrontRight;
case 5:
case 6:
return kBackDual;
default:
return 0;
}
}
if (bus_num == 2) {
switch (port_num) {
case 1:
return kFrontLeft;
case 2:
return kFrontRight;
case 3:
case 4:
return kBackDual;
default:
return 0;
}
}
return 0;
}
bool GetDevice(uint16_t vid, uint16_t pid, libusb_device **device,
std::string *err_msg) {
libusb_context *context;
libusb_device **device_list;
*device = nullptr;
if (libusb_init(&context)) { // On failure.
*err_msg = "Failed to get context";
return false;
}
int num_devices = 0;
if ((num_devices = libusb_get_device_list(context, &device_list)) < 0) {
*err_msg = "Failed to get USB devices";
return false;
}
for (int i = 0; i < num_devices; i++) {
libusb_device_descriptor descriptor;
// If device descriptor can be acquired, and acquired
// descriptor belongs to desired device, return true.
if (!libusb_get_device_descriptor(device_list[i], &descriptor) &&
vid == descriptor.idVendor && pid == descriptor.idProduct) {
*device = device_list[i];
libusb_ref_device(*device);
}
}
libusb_free_device_list(device_list, 1);
return *device != nullptr;
}
bool HotplugDeviceGuado(uint16_t vid, uint16_t pid, FILE* stream) {
// Guado uses /sys/class/gpio/* to control VBUS power to the USB ports.
libusb_device *huddly = nullptr;
std::string err_msg = "";
if (!GetDevice(vid, pid, &huddly, &err_msg)) {
LOG(INFO) << "Failed to find camera. Cannot hotplug.";
if (!err_msg.empty()) LOG(ERROR) << err_msg;
return false;
}
uint8_t bus_num = libusb_get_bus_number(huddly);
uint8_t port_num = libusb_get_port_number(huddly);
uint32_t gpio_num = GetGpioNumGuado(bus_num, port_num);
if (!gpio_num) { // unknown gpio number.
LOG(ERROR) << "Failed to get gpio number.";
return false;
}
std::string gpio_string = std::to_string(gpio_num);
// Set direction of byte flow in gpio.
std::string out_string = "out";
base::FilePath direction_path("/sys/class/gpio/gpio" + gpio_string +
"/direction");
if (base::WriteFile(direction_path, out_string.c_str(), out_string.length()) <
0) {
LOG(WARNING) << "Failed to set GPIO direction.";
return false;
}
// Turn off power going to gpio.
std::string toggle = "0";
base::FilePath toggle_path("/sys/class/gpio/gpio" + gpio_string + "/value");
if (base::WriteFile(toggle_path, toggle.c_str(), toggle.length()) < 0) {
LOG(WARNING) << "Failed to cut GPIO power.";
return false;
}
// Consume and discard all further input to avoid triggering restarts for
// things that happened before the power down.
ConsumeAndDiscardInput(stream);
// Bring back power going to gpio.
toggle = "1";
if (base::WriteFile(toggle_path, toggle.c_str(), toggle.length()) < 0) {
LOG(WARNING) << "Failed to turn on GPIO.";
return false;
}
return true;
}
} // namespace huddly_monitor