blob: b5e9b631565f8183c8f2529d6e74a8b7327701b2 [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2016 Herwig Eichler, www.conmeleon.org
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License 2.0 which is available at
* http://www.eclipse.org/legal/epl-2.0.
*
* SPDX-License-Identifier: EPL-2.0
*
* Contributors:
* Herwig Eichler - initial API and implementation and initial documentation
*******************************************************************************/
#include "gpiopin.h"
#include "../util/fileres.h"
#include <string>
#include <iostream>
#include "../../../core/utils/criticalregion.h"
namespace CONMELEON {
CPCSyncObject CGpioPin::m_GlobalFileMutex;
CGpioPin::CGpioPin(int iPinNr, EPinDirection enDir) : m_Nr(iPinNr), m_Valid(iPinNr > 0), m_Inverted(false), m_Direction(enDir), m_State(unused){
if (iPinNr > 0) {
m_Valid = this->sysfsExportPin() && this->sysfsSetPinDirection(enDir) && this->sysfsOpenValueFileStream(enDir);
}
}
CGpioPin::~CGpioPin() {
this->sysfsUnexportPin();
// the value file stream does not need to be closed here, this will be handled by the fstream destructor.
}
bool CGpioPin::sysfsExportPin() const {
char szPinNr[12] = {0};
sprintf(szPinNr, "%d", m_Nr);
/* protect sysfs export file from multiple access
* all class instances need access to the same export file, that's why a global class mutex needs to be used
*/
bool bRet;
{
CCriticalRegion criticalRegion(m_GlobalFileMutex);
bRet = writeToFile(ExportFilePath, &szPinNr[0]);
}
return bRet;
// TODO: if an exception is thrown within writeToFile(), the mutex might be left locked
}
bool CGpioPin::sysfsUnexportPin() const {
char szPinNr[12] = {0};
sprintf(szPinNr, "%d", m_Nr);
bool bRet;
{
CCriticalRegion criticalRegion(m_GlobalFileMutex);
bRet = writeToFile(UnexportFilePath, &szPinNr[0]);
}
return bRet;
}
bool CGpioPin::sysfsSetPinDirection(EPinDirection enDir) {
char szFilename[40] = {0};
bool bRet = false;
sprintf(szFilename, "%s%d%s", SignalFilePathPrefix, m_Nr, DirFilePathPostfix);
/* only this class instance needs access to the sysfs direction file
* we can use the local mutex for protection
*/
{
CCriticalRegion criticalRegion(m_LocalFileMutex);
if((enDir == input) && (writeToFile(&szFilename[0], "in"))){
bRet = true;
}
if((enDir == output) && (writeToFile(&szFilename[0], "out"))){
bRet = true;
}
}
return bRet;
}
bool CGpioPin::sysfsOpenValueFileStream(EPinDirection enDir) {
char szFilename[40] = {0};
sprintf(szFilename, "%s%d%s", SignalFilePathPrefix, m_Nr, ValFilePathPostfix);
/* only this class instance needs access to the sysfs value file
* we can use the local mutex for protection
*/
{
CCriticalRegion criticalRegion(m_LocalFileMutex);
m_PinValueStream.open(szFilename, ((enDir == input) ? std::ios_base::in : std::ios_base::out));
}
return m_PinValueStream.is_open();
}
void CGpioPin::setInverted(bool bInverted) {
m_Inverted = bInverted && (m_Direction == input);
}
bool CGpioPin::read() const {
if (m_Valid && (m_Direction == input)) {
if (m_PinValueStream.is_open()) {
std::string sLine;
CCriticalRegion criticalRegion(m_LocalFileMutex);
/* set to beginning of file, just to be sure */
m_PinValueStream.clear();
m_PinValueStream.seekg(0, std::ios::beg);
m_PinValueStream >> sLine;
return ((sLine != "0") ^ (m_Inverted));
}
}
return false;
}
void CGpioPin::write(bool bValue) {
if (m_Valid && (m_Direction == output)) {
CCriticalRegion criticalRegion(m_LocalFileMutex);
if(m_PinValueStream.is_open()){
m_PinValueStream << (bValue ? "1" : "0") << std::flush;
}
}
}
bool readFromFile(const char* pszFileName, char* pszContent, std::size_t nBufferLength ) {
if ((pszFileName == nullptr) || (pszContent == nullptr) || (nBufferLength < 1)){
return false;
}
CFileResource file(pszFileName, "r");
if (file.isOpen()) {
file.readLine(pszContent, nBufferLength);
// TODO: add error handling
return true;
}
else {
*pszContent = '\0';
return false;
}
// no need to close the file manually, because this is handled by the destructor of CFileResource class (RAII)
}
bool writeToFile(const char* pszFileName, const char* pszContent) {
if (pszFileName == nullptr) {
return false;
}
CFileResource file(pszFileName, "a");
if (file.isOpen() && (pszContent != nullptr)) {
file.writeLine(pszContent);
// TODO: add error handling
}
return true;
}
} // namespace conmeleon