From f70f9c76555ed6186173e5cb93d4c79f6c3e1e4f Mon Sep 17 00:00:00 2001 From: Bertrand SIMONNET Date: Tue, 26 Apr 2016 16:50:43 -0700 Subject: Implement the backend for UART support. This includes: * the hal hooks to register UART devices. * the UART sysfs driver. * the UART manager. Bug: 27898961 Change-Id: I48488b2684c9198c70a908cef3ef6c2032eb4bfe --- daemon/Android.mk | 2 + daemon/peripheral_manager.cc | 22 +++++ daemon/uart_driver.h | 74 ++++++++++++++++ daemon/uart_driver_sysfs.cc | 204 +++++++++++++++++++++++++++++++++++++++++++ daemon/uart_driver_sysfs.h | 59 +++++++++++++ daemon/uart_manager.cc | 119 +++++++++++++++++++++++++ daemon/uart_manager.h | 93 ++++++++++++++++++++ hal/hardware/peripheral_io.h | 24 +++++ 8 files changed, 597 insertions(+) create mode 100644 daemon/uart_driver.h create mode 100644 daemon/uart_driver_sysfs.cc create mode 100644 daemon/uart_driver_sysfs.h create mode 100644 daemon/uart_manager.cc create mode 100644 daemon/uart_manager.h diff --git a/daemon/Android.mk b/daemon/Android.mk index 6e8c6ed..935ee62 100644 --- a/daemon/Android.mk +++ b/daemon/Android.mk @@ -43,6 +43,8 @@ libperipheralman_internal_CommonSources := \ pin_mux_manager.cc \ spi_driver_spidev.cc \ spi_manager.cc \ + uart_driver_sysfs.cc \ + uart_manager.cc \ # peripheralman executable # ======================================================== diff --git a/daemon/peripheral_manager.cc b/daemon/peripheral_manager.cc index c946932..ba8c6aa 100644 --- a/daemon/peripheral_manager.cc +++ b/daemon/peripheral_manager.cc @@ -30,6 +30,8 @@ #include "led_manager.h" #include "pin_mux_manager.h" #include "spi_driver_spidev.h" +#include "uart_driver_sysfs.h" +#include "uart_manager.h" namespace android { namespace { @@ -57,6 +59,15 @@ static int RegisterLedSysfs(const char* name, const char* sysfs_name) { return LedManager::GetLedManager()->RegisterLedSysfs(name, sysfs_name); } +// Uart callbacks +static int RegisterUartBus(const char* name, const char* dev_name) { + return UartManager::GetManager()->RegisterUartDevice(name, dev_name); +} + +static int SetUartPinMux(const char* name, const char* source) { + return UartManager::GetManager()->SetPinMux(name, source); +} + // I2c callbacks static int RegisterI2cDevBus(const char* name, uint32_t bus) { return I2cManager::GetI2cManager()->RegisterI2cDevBus(name, bus); @@ -158,6 +169,13 @@ bool PeripheralManager::RegisterDrivers() { LOG(ERROR) << "Failed to load driver: LedDriverSysfs"; return false; } + if (!UartManager::GetManager()->RegisterDriver( + std::unique_ptr( + new UartDriverInfo( + nullptr)))) { + LOG(ERROR) << "Failed to load driver: UartDriverSysfs"; + return false; + } return true; } @@ -183,6 +201,10 @@ bool PeripheralManager::InitHal() { // Led .register_led_sysfs = RegisterLedSysfs, + // Uart + .register_uart_bus = RegisterUartBus, + .set_uart_pin_mux = SetUartPinMux, + // I2c .register_i2c_dev_bus = RegisterI2cDevBus, .set_i2c_pin_mux = SetI2cPinMux, diff --git a/daemon/uart_driver.h b/daemon/uart_driver.h new file mode 100644 index 0000000..7e951a8 --- /dev/null +++ b/daemon/uart_driver.h @@ -0,0 +1,74 @@ +/* + * Copyright (C) 2016 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef SYSTEM_PERIPHERALMANAGER_DAEMON_UART_DRIVER_H_ +#define SYSTEM_PERIPHERALMANAGER_DAEMON_UART_DRIVER_H_ + +#include + +#include +#include +#include + +#include +#include + +namespace android { + +class UartDriverInterface { + public: + virtual ~UartDriverInterface() {} + + // TODO(leecam): Init should have generic params. + virtual bool Init(const std::string& name) = 0; + + virtual int SetBaudrate(uint32_t baudrate) = 0; + + virtual int Write(const std::vector& data, + uint32_t* bytes_written) = 0; + + virtual int Read(std::vector* data, + uint32_t size, + uint32_t* bytes_read) = 0; +}; + +class UartDriverInfoBase { + public: + virtual ~UartDriverInfoBase() {} + + virtual std::string Compat() = 0; + virtual std::unique_ptr Probe() = 0; +}; + +template +class UartDriverInfo : public UartDriverInfoBase { + public: + UartDriverInfo(PARAM param) : param_(param) {} + ~UartDriverInfo() override {} + + std::string Compat() override { return T::Compat(); } + + std::unique_ptr Probe() override { + return std::unique_ptr(new T(param_)); + } + + private: + PARAM param_; +}; + +} // namespace android + +#endif // SYSTEM_PERIPHERALMANAGER_DAEMON_UART_DRIVER_H_ diff --git a/daemon/uart_driver_sysfs.cc b/daemon/uart_driver_sysfs.cc new file mode 100644 index 0000000..41e3064 --- /dev/null +++ b/daemon/uart_driver_sysfs.cc @@ -0,0 +1,204 @@ +/* + * Copyright (C) 2016 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "uart_driver_sysfs.h" + +#include +#include + +#include + +#include "char_device.h" + +namespace android { + +UartDriverSysfs::UartDriverSysfs(CharDeviceFactory* factory) + : char_device_factory_(factory) {} + +UartDriverSysfs::~UartDriverSysfs() {} + +bool UartDriverSysfs::Init(const std::string& name) { + path_ = name; + + // Get a char device. If char_device_factory_ is set + // then this is a unittest and the char device is provided + // by the test. Otherwise create a normal CharDevice. + if (!char_device_factory_) { + char_interface_.reset(new CharDevice()); + } else { + char_interface_ = char_device_factory_->NewCharDevice(); + } + + // Open as non-blocking as we don't want peripheral_manager to block on a + // single client read. + int fd = char_interface_->Open(path_.c_str(), O_RDWR | O_NONBLOCK); + if (fd < 0) { + PLOG(WARNING) << "Failed to open " << path_; + return false; + } + + // Configure the device in raw mode. + struct termios config; + if (char_interface_->Ioctl(fd, TCGETS, &config)) { + PLOG(ERROR) << "Failed to read the tty config"; + close(fd); + return false; + } + cfmakeraw(&config); + + if (char_interface_->Ioctl(fd, TCSETSF, &config)) { + PLOG(ERROR) << "Failed to configure the UART device as Raw."; + close(fd); + return false; + } + + fd_ = fd; + return true; +} + +int UartDriverSysfs::SetBaudrate(uint32_t baudrate) { + speed_t s; + switch (baudrate) { + case 0: + s = B0; + break; + + case 50: + s = B50; + break; + + case 75: + s = B75; + break; + + case 110: + s = B110; + break; + + case 134: + s = B134; + break; + + case 150: + s = B150; + break; + + case 200: + s = B200; + break; + + case 300: + s = B300; + break; + + case 600: + s = B600; + break; + + case 1200: + s = B1200; + break; + + case 1800: + s = B1800; + break; + + case 2400: + s = B2400; + break; + + case 4800: + s = B4800; + break; + + case 9600: + s = B9600; + break; + + case 19200: + s = B19200; + break; + + case 38400: + s = B38400; + break; + + case 57600: + s = B57600; + break; + + case 115200: + s = B115200; + break; + + case 230400: + s = B230400; + break; + default: + return EINVAL; + } + + struct termios config; + + if (char_interface_->Ioctl(fd_, TCGETS, &config) != 0 || + cfsetspeed(&config, s) != 0 || + char_interface_->Ioctl(fd_, TCSETS, &config) != 0) { + LOG(ERROR) << "Failed to set the UART baurate to " << baudrate; + return EIO; + } + + return 0; +} + +int UartDriverSysfs::Write(const std::vector& data, + uint32_t* bytes_written) { + errno = 0; + int ret = char_interface_->Write(fd_, data.data(), data.size()); + + if (ret == -1) { + PLOG(ERROR) << "Failed to write to UART device"; + *bytes_written = 0; + return EIO; + } + + *bytes_written = ret; + return 0; +} + +int UartDriverSysfs::Read(std::vector* data, + uint32_t size, + uint32_t* bytes_read) { + errno = 0; + data->resize(size); + + int ret = char_interface_->Read(fd_, data->data(), size); + + if (ret == -1) { + *bytes_read = 0; + data->resize(0); + if (errno == EAGAIN) { + return EAGAIN; + } + PLOG(ERROR) << "Failed to read from UART device"; + return EIO; + } + + *bytes_read = ret; + data->resize(ret); + return 0; +} + +} // namespace android diff --git a/daemon/uart_driver_sysfs.h b/daemon/uart_driver_sysfs.h new file mode 100644 index 0000000..d8f6e16 --- /dev/null +++ b/daemon/uart_driver_sysfs.h @@ -0,0 +1,59 @@ +/* + * Copyright (C) 2016 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef SYSTEM_PERIPHERALMANAGER_DAEMON_UART_DRIVER_SYSFS_H_ +#define SYSTEM_PERIPHERALMANAGER_DAEMON_UART_DRIVER_SYSFS_H_ + +#include + +#include +#include + +#include "char_device.h" +#include "uart_driver.h" + +namespace android { + +class UartDriverSysfs : public UartDriverInterface { + public: + UartDriverSysfs(CharDeviceFactory* factory); + ~UartDriverSysfs(); + + static std::string Compat() { return "UARTSYSFS"; } + + bool Init(const std::string& name) override; + + int SetBaudrate(uint32_t baudrate) override; + + int Write(const std::vector& data, uint32_t* bytes_written) override; + + int Read(std::vector* data, + uint32_t size, + uint32_t* bytes_read) override; + + private: + int fd_; + std::string path_; + + CharDeviceFactory* char_device_factory_; + std::unique_ptr char_interface_; + + DISALLOW_COPY_AND_ASSIGN(UartDriverSysfs); +}; + +} // namespace android + +#endif // SYSTEM_PERIPHERALMANAGER_DAEMON_UART_DRIVER_SYSFS_H_ diff --git a/daemon/uart_manager.cc b/daemon/uart_manager.cc new file mode 100644 index 0000000..bc98111 --- /dev/null +++ b/daemon/uart_manager.cc @@ -0,0 +1,119 @@ +/* + * Copyright (C) 2016 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "uart_manager.h" + +#include "uart_driver_sysfs.h" + +#include + +namespace android { + +std::unique_ptr g_uart_manager; + +UartManager* UartManager::GetManager() { + if (!g_uart_manager) { + g_uart_manager.reset(new UartManager); + } + return g_uart_manager.get(); +} + +void UartManager::ResetManager() { + g_uart_manager.reset(); +} + +UartManager::UartManager() {} + +UartManager::~UartManager() {} + +bool UartManager::RegisterDriver( + std::unique_ptr driver_info) { + std::string key = driver_info->Compat(); + driver_infos_[key] = std::move(driver_info); + return true; +} + +bool UartManager::RegisterUartDevice(const std::string& name, + const std::string& uart_device_name) { + if (uart_devices_.count(name)) { + LOG(ERROR) << "Uart device " << name << " is already registered"; + return false; + } + uart_devices_[name].name = name; + uart_devices_[name].path = uart_device_name; + return true; +} + +bool UartManager::SetPinMux(const std::string& name, + const std::string& pin_mux) { + auto bus_it = uart_devices_.find(name); + if (bus_it == uart_devices_.end()) { + LOG(ERROR) << "Uart device " << name << " is not registered"; + return false; + } + + bus_it->second.mux = pin_mux; + return true; +} + +std::vector UartManager::GetDevicesList() { + std::vector list; + for (const auto& it : uart_devices_) { + list.push_back(it.first); + } + return list; +} + +bool UartManager::HasUartDevice(const std::string& name) { + return uart_devices_.count(name); +} + +std::unique_ptr UartManager::OpenUartDevice( + const std::string& name) { + // Get the Bus from the BSP. + auto bus_it = uart_devices_.find(name); + if (bus_it == uart_devices_.end()) + return nullptr; + + // Check its not already in use + if (bus_it->second.driver_) + return nullptr; + + // Find a driver. + // Currently there is only hardcoded support for UARTSYSFS + auto driver_info_it = driver_infos_.find(UartDriverSysfs::Compat()); + + // Fail if there is no driver. + if (driver_info_it == driver_infos_.end()) + return nullptr; + + std::unique_ptr driver(driver_info_it->second->Probe()); + + if (!driver->Init(bus_it->second.path)) + return nullptr; + + // Set Pin muxing. + if (!bus_it->second.mux.empty()) { + PinMuxManager::GetPinMuxManager()->SetSource(bus_it->second.mux, + bus_it->second.mux); + } + + bus_it->second.driver_ = std::move(driver); + + return std::unique_ptr(new UartDevice(&(bus_it->second))); +} + +} // namespace android diff --git a/daemon/uart_manager.h b/daemon/uart_manager.h new file mode 100644 index 0000000..b304ea1 --- /dev/null +++ b/daemon/uart_manager.h @@ -0,0 +1,93 @@ +/* + * Copyright (C) 2016 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef SYSTEM_PERIPHERALMANAGER_DAEMON_UART_MANAGER_H_ +#define SYSTEM_PERIPHERALMANAGER_DAEMON_UART_MANAGER_H_ + +#include + +#include +#include +#include +#include + +#include +#include + +#include "pin_mux_manager.h" +#include "uart_driver.h" + +namespace android { + +struct UartSysfs { + std::string name; + std::string path; + std::string mux; + std::unique_ptr driver_; +}; + +class UartDevice { + public: + UartDevice(UartSysfs* uart_device) : uart_device_(uart_device) {} + ~UartDevice() { uart_device_->driver_.reset(); } + + int SetBaudrate(uint32_t baudrate) { + return uart_device_->driver_->SetBaudrate(baudrate); + } + + int Write(const std::vector& data, uint32_t* bytes_written) { + return uart_device_->driver_->Write(data, bytes_written); + } + + int Read(std::vector* data, uint32_t size, uint32_t* bytes_read) { + return uart_device_->driver_->Read(data, size, bytes_read); + } + + private: + UartSysfs* uart_device_; +}; + +class UartManager { + public: + ~UartManager(); + + // Get the singleton. + static UartManager* GetManager(); + static void ResetManager(); + + // Used by the BSP to tell PMan of an sysfs uart_device. + bool RegisterUartDevice(const std::string& name, const std::string& path); + bool SetPinMux(const std::string& name, const std::string& mux); + + std::vector GetDevicesList(); + bool HasUartDevice(const std::string& name); + + bool RegisterDriver(std::unique_ptr driver_info); + + std::unique_ptr OpenUartDevice(const std::string& name); + + private: + UartManager(); + + std::map> driver_infos_; + std::map uart_devices_; + + DISALLOW_COPY_AND_ASSIGN(UartManager); +}; + +} // namespace android + +#endif // SYSTEM_PERIPHERALMANAGER_DAEMON_UART_MANAGER_H_ diff --git a/hal/hardware/peripheral_io.h b/hal/hardware/peripheral_io.h index 1de31ef..624e83b 100644 --- a/hal/hardware/peripheral_io.h +++ b/hal/hardware/peripheral_io.h @@ -212,6 +212,30 @@ typedef struct peripheral_registration_cb_t { */ int (*register_led_sysfs)(const char* name, const char* sysfs_name); + /** + * Register a UART bus. + * + * Args: + * name: Friendly name of the bus. + * dev_name: Name of the device in sysfs. + * + * Returns: + * 0 on success, errno on error. + */ + int (*register_uart_bus)(const char* name, const char* dev_name); + + /** + * Set the pinmux for a given UART bus. + * + * Args: + * name: Friendly name of the UART bus. + * source: Name of the pinmuxing source. + * + * Returns: + * 0 on success, errno on error. + */ + int (*set_uart_pin_mux)(const char* name, const char* source); + /** * Register an I2C bus. * -- cgit v1.2.3