blob: 2ac48447b210d184022c99129a1f6742a699ac2c [file] [log] [blame]
/*
* 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 "spi_driver_spidev.h"
#include <fcntl.h>
#include <sys/ioctl.h>
#include <linux/spi/spidev.h>
#include <string>
#include <base/logging.h>
namespace android {
namespace {
const char kSpiDevPath[] = "/dev/spidev";
} // namespace
SpiDriverSpiDev::SpiDriverSpiDev(CharDeviceFactory* char_device_factory)
: fd_(-1), char_device_factory_(char_device_factory) {}
SpiDriverSpiDev::~SpiDriverSpiDev() {
if (fd_ >= 0 && char_interface_ != nullptr) {
char_interface_->Close(fd_);
}
}
bool SpiDriverSpiDev::Init(uint32_t bus_id, uint32_t cs) {
if (fd_ >= 0) {
return false;
}
// 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();
}
std::string path =
kSpiDevPath + std::to_string(bus_id) + "." + std::to_string(cs);
int fd = char_interface_->Open(path.c_str(), O_RDWR);
if (fd < 0)
return false;
fd_ = fd;
// Set the default speed to the max
uint32_t max_freq = 0;
if (!GetMaxFrequency(&max_freq)) {
char_interface_->Close(fd_);
fd_ = -1;
return false;
}
speed_hz_ = max_freq;
// Default to 0 microseconds delay between transfers.
delay_usecs_ = 0;
// Default to 8 bits per word.
bits_per_word_ = 8;
return true;
}
bool SpiDriverSpiDev::Transfer(const void* tx_data, void* rx_data, size_t len) {
struct spi_ioc_transfer msg;
memset(&msg, 0, sizeof(msg));
msg.tx_buf = (unsigned long)tx_data;
msg.rx_buf = (unsigned long)rx_data;
msg.speed_hz = speed_hz_;
msg.bits_per_word = bits_per_word_;
msg.delay_usecs = delay_usecs_;
msg.len = len;
if (char_interface_->Ioctl(fd_, SPI_IOC_MESSAGE(1), &msg) < 0) {
PLOG(ERROR) << "SPI Transfer IOCTL Failed";
return false;
}
return true;
}
bool SpiDriverSpiDev::SetFrequency(uint32_t speed_hz) {
if (fd_ < 0)
return false;
// Get the max speed and ensure speed_hz is less than it.
uint32_t max_speed = 0;
if (!GetMaxFrequency(&max_speed))
return false;
if (speed_hz > max_speed)
speed_hz = max_speed;
speed_hz_ = speed_hz;
return true;
}
bool SpiDriverSpiDev::GetMaxFrequency(uint32_t* max_freq) {
if (char_interface_->Ioctl(fd_, SPI_IOC_RD_MAX_SPEED_HZ, max_freq) < 0) {
PLOG(ERROR) << "Failed to get spi max freq";
return false;
}
return true;
}
bool SpiDriverSpiDev::SetMode(SpiMode mode) {
uint8_t k_mode = 0;
switch (mode) {
case kMode0:
k_mode = SPI_MODE_0;
break;
case kMode1:
k_mode = SPI_MODE_1;
break;
case kMode2:
k_mode = SPI_MODE_2;
break;
case kMode3:
k_mode = SPI_MODE_3;
break;
}
if (char_interface_->Ioctl(fd_, SPI_IOC_WR_MODE, &k_mode) < 0) {
PLOG(ERROR) << "Failed to set mode";
return false;
}
return true;
}
bool SpiDriverSpiDev::SetBitJustification(bool lsb_first) {
uint8_t k_lsb_first = lsb_first;
if (char_interface_->Ioctl(fd_, SPI_IOC_WR_LSB_FIRST, &k_lsb_first) < 0) {
PLOG(ERROR) << "Failed to set bit justifcation";
return false;
}
return true;
}
bool SpiDriverSpiDev::SetBitsPerWord(uint8_t bits_per_word) {
if (char_interface_->Ioctl(fd_, SPI_IOC_WR_BITS_PER_WORD, &bits_per_word) <
0) {
PLOG(ERROR) << "Failed to set bits per word";
return false;
}
bits_per_word_ = bits_per_word;
return true;
}
bool SpiDriverSpiDev::SetDelay(uint16_t delay_usecs) {
delay_usecs_ = delay_usecs;
return true;
}
} // namespace android