diff --git a/ChangeLog b/ChangeLog index bea8721e..627a25f6 100644 --- a/ChangeLog +++ b/ChangeLog @@ -15797,3 +15797,7 @@ 2017-05-19 Fred Gleason * Added support for the 'GPO Set' ['GO'] RML to the Modbus driver in 'ripcd/modbus.cpp' and 'ripcd/modbus.h'. +2017-05-23 Fred Gleason + * Added an 'RDMatrix::KernelGpio' element to the 'RDMatrix::Type enum. + * Implemented a Kernel GPIO switcher driver in 'ripcd/kernelgpio.cpp' + and 'ripcd/kernelgpio.h'. diff --git a/docs/SWITCHERS.txt b/docs/SWITCHERS.txt index bf5bbbf9..db7935e5 100644 --- a/docs/SWITCHERS.txt +++ b/docs/SWITCHERS.txt @@ -19,6 +19,7 @@ Broadcast Tools SS 4.4 Audio Switcher Broadcast Tools ACS 8.2 Audio Control Switcher Broadcast Tools SS 8.2 Audio Switcher Harlond Virtual Mixer +Kernel GPIO LiveWire LWRP Audio LiveWire LWRP GPIO LiveWire Multicast GPIO @@ -465,6 +466,25 @@ Input channel ON/OFF is sensed/controlled by means of the respective GPIO number using GE/GI/GO RMLs. +---------------------------------------------------------------------------- +Kernel GPIO + +Driver Name: Kernel GPIO + +Supported RML Commands: + GPI Enable ('GE') + GPI Set ('GI') + GPO Set ('GO') + +GENERAL NOTES: +This driver can be used to control kernel-based GPIO interfaces, such as +the GPIO pins on a Raspberry Pi. Pins are allocated such that inputs come +first, followed by outputs. + +Further information on kernel GPIO devices can be found at +https://www.kernel.org/doc/Documentation/gpio/sysfs.txt + + ---------------------------------------------------------------------------- LiveWire LWRP Audio diff --git a/lib/Makefile.am b/lib/Makefile.am index db79f32f..f6c8a6f8 100644 --- a/lib/Makefile.am +++ b/lib/Makefile.am @@ -151,6 +151,7 @@ dist_librd_la_SOURCES = dbversion.h\ rdintegeredit.cpp rdintegeredit.h\ rdintegerdialog.cpp rdintegerdialog.h\ rd.h\ + rdkernelgpio.cpp rdkernelgpio.h\ rdlabel.cpp rdlabel.h\ rdlibrary_conf.cpp rdlibrary_conf.h\ rdlicense.cpp rdlicense.h\ @@ -287,6 +288,7 @@ nodist_librd_la_SOURCES = moc_rdadd_cart.cpp\ moc_rdimport_audio.cpp\ moc_rdintegeredit.cpp\ moc_rdintegerdialog.cpp\ + moc_rdkernelgpio.cpp\ moc_rdlabel.cpp\ moc_rdlicense.cpp\ moc_rdlineedit.cpp\ diff --git a/lib/lib.pro b/lib/lib.pro index 0a2a5f19..42e0b5a4 100644 --- a/lib/lib.pro +++ b/lib/lib.pro @@ -151,6 +151,7 @@ x11 { SOURCES += rdgroup.cpp SOURCES += rdhash.cpp SOURCES += rdimport_audio.cpp + SOURCES += rdkernelgpio.cpp SOURCES += rdlist_groups.cpp SOURCES += rdlist_logs.cpp SOURCES += rdmarker_button.cpp @@ -286,6 +287,7 @@ x11 { HEADERS += rdgroup.h HEADERS += rdhash.h HEADERS += rdimport_audio.h + HEADERS += rdkernelgpio.h HEADERS += rdlist_groups.h HEADERS += rdlist_logs.h HEADERS += rdmarker_button.h diff --git a/lib/rdkernelgpio.cpp b/lib/rdkernelgpio.cpp new file mode 100644 index 00000000..e6530eef --- /dev/null +++ b/lib/rdkernelgpio.cpp @@ -0,0 +1,217 @@ +// rdkernelgpio.cpp +// +// Control Class for the Linux SysFS GPIO Interface +// +// (C) Copyright 2017 Fred Gleason +// +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU Library General Public License +// version 2 as published by the Free Software Foundation. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public +// License along with this program; if not, write to the Free Software +// Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +// + +#include "rdkernelgpio.h" + +RDKernelGpio::RDKernelGpio(QObject *parent) + : QObject(parent) +{ + gpio_poll_timer=new QTimer(this); + connect(gpio_poll_timer,SIGNAL(timeout()),this,SLOT(pollData())); +} + + +RDKernelGpio::~RDKernelGpio() +{ + for(unsigned i=0;istart(KERNELGPIO_POLL_INTERVAL); + } + + return true; +} + + +bool RDKernelGpio::removeGpio(int gpio) +{ + FILE *f=NULL; + + if((f=OpenNode("unexport","w"))==NULL) { + return false; + } + fprintf(f,"%u",gpio); + fclose(f); + + return true; +} + + +RDKernelGpio::Direction RDKernelGpio::direction(int gpio,bool *ok) const +{ + RDKernelGpio::Direction ret=RDKernelGpio::In; + FILE *f=NULL; + char str[255]; + + if((f=OpenNode("direction","r",gpio))!=NULL) { + fscanf(f,"%s",str); + if(QString(str)=="out") { + ret=RDKernelGpio::Out; + } + fclose(f); + if(ok!=NULL) { + *ok=true; + } + } + else { + if(ok!=NULL) { + *ok=false; + } + } + + return ret; +} + + +bool RDKernelGpio::setDirection(int gpio,RDKernelGpio::Direction dir) const +{ + FILE *f=NULL; + + if((f=OpenNode("direction","w",gpio))!=NULL) { + switch(dir) { + case RDKernelGpio::In: + fprintf(f,"in"); + break; + + case RDKernelGpio::Out: + fprintf(f,"out"); + break; + } + fclose(f); + return true; + } + return false; +} + + +bool RDKernelGpio::activeLow(int gpio,bool *ok) const +{ + unsigned ret=false; + FILE *f=NULL; + + if((f=OpenNode("active_low","r",gpio))!=NULL) { + fscanf(f,"%u",&ret); + fclose(f); + if(ok!=NULL) { + *ok=true; + } + } + else { + if(ok!=NULL) { + *ok=false; + } + } + + return (bool)ret; +} + + +bool RDKernelGpio::setActiveLow(int gpio,bool state) const +{ + FILE *f=NULL; + + if((f=OpenNode("active_low","w",gpio))!=NULL) { + fprintf(f,"%u",state); + fclose(f); + return true; + } + return false; +} + + +bool RDKernelGpio::value(int gpio,bool *ok) const +{ + unsigned ret=false; + FILE *f=NULL; + + if((f=OpenNode("value","r",gpio))!=NULL) { + fscanf(f,"%u",&ret); + fclose(f); + if(ok!=NULL) { + *ok=true; + } + } + else { + if(ok!=NULL) { + *ok=false; + } + } + + return (bool)ret; +} + + +bool RDKernelGpio::setValue(int gpio,bool state) const +{ + FILE *f=NULL; + + if((f=OpenNode("value","w",gpio))!=NULL) { + fprintf(f,"%u",state); + fclose(f); + return true; + } + return false; +} + + +void RDKernelGpio::pollData() +{ + bool state=false; + + for(unsigned i=0;i +// +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU Library General Public License +// version 2 as published by the Free Software Foundation. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public +// License along with this program; if not, write to the Free Software +// Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +// + +#ifndef RDKERNELGPIO_H +#define RDKERNELGPIO_H + +#include + +#include + +#include +#include + +// +// See https://www.kernel.org/doc/Documentation/gpio/sysfs.txt +// for an explanation of this interface. +// +#define KERNELGPIO_SYS_FILE QString("/sys/class/gpio") +#define KERNELGPIO_POLL_INTERVAL 20 + +class RDKernelGpio : public QObject +{ + Q_OBJECT; + public: + enum Direction {In=0,Out=1}; + enum Edge {None=0,Rising=1,Falling=2,Both=3}; + RDKernelGpio(QObject *parent=0); + ~RDKernelGpio(); + bool addGpio(int gpio); + bool removeGpio(int gpio); + Direction direction(int gpio, bool *ok=NULL) const; + bool setDirection(int gpio,Direction dir) const; + bool activeLow(int gpio, bool *ok=NULL) const; + bool setActiveLow(int gpio,bool state) const; + bool value(int gpio,bool *ok=NULL) const; + + public slots: + bool setValue(int gpio,bool state) const; + + private slots: + void pollData(); + + signals: + void valueChanged(int gpio,bool state); + + private: + FILE *OpenNode(const QString &name,const char *mode,int gpio=-1) const; + std::vector gpio_gpios; + std::vector gpio_states; + QTimer *gpio_poll_timer; +}; + + +#endif // RDKERNELGPIO_H diff --git a/lib/rdmatrix.cpp b/lib/rdmatrix.cpp index 2941c8f9..e9782744 100644 --- a/lib/rdmatrix.cpp +++ b/lib/rdmatrix.cpp @@ -67,7 +67,8 @@ bool __mx_primary_controls[RDMatrix::LastType][RDMatrix::LastControl]= {0,1,0,0,0,0,0,0,1,0,0,1,1,0,0,0,1,1,0,0,0,0,0,0,0,0,0,0,0}, // Ross NK/SCP {0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,1,1,0,0,0,0,0,0,0,0,0}, // BT ADMS 44.22 {0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,1,1,0,0,0,0,0,0,0,0,0}, // BT SS 4.1 MLR - {0,0,1,1,0,0,0,0,0,0,0,0,0,1,1,0,0,0,1,1,0,0,0,0,0,0,0,0,0} // Modbus + {0,0,1,1,0,0,0,0,0,0,0,0,0,1,1,0,0,0,1,1,0,0,0,0,0,0,0,0,0}, // Modbus + {0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,0,0,0,1,1,0,0,0,0,0,0,0,0,0} // Kernel GPIO }; bool __mx_backup_controls[RDMatrix::LastType][RDMatrix::LastControl]= { @@ -111,7 +112,8 @@ bool __mx_backup_controls[RDMatrix::LastType][RDMatrix::LastControl]= {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}, // ROSS NK/SCP {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}, // BT ADMS 44.22 {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}, // BT SS 4.1 MLR - {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0} // Modbus + {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}, // Modbus + {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0} // Kernel GPIO }; int __mx_default_values[RDMatrix::LastType][RDMatrix::LastControl]= @@ -156,7 +158,8 @@ int __mx_default_values[RDMatrix::LastType][RDMatrix::LastControl]= {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0}, // Ross NK/SCP {0,0,0,0,0,0,0,0,0,0,0,8,2,16,13,0,0,0,0,0,0,0,0,0,0,0,0,1,0}, // BT ADMS 44.22 {0,0,0,0,0,0,0,0,0,0,0,4,1,8,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0}, // BT SS 4.1 MLR - {1,0,0,502,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0} // Modbus + {1,0,0,502,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0}, // Modbus + {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0} // Kernel GPIO }; RDMatrix::RDMatrix(const QString &station,int matrix) @@ -724,6 +727,10 @@ QString RDMatrix::typeString(RDMatrix::Type type) return QString("Modbus TCP"); break; + case RDMatrix::KernelGpio: + return QString("Kernel GPIO"); + break; + default: return QString("Unknown Type"); break; diff --git a/lib/rdmatrix.h b/lib/rdmatrix.h index fa9866af..e7319ee3 100644 --- a/lib/rdmatrix.h +++ b/lib/rdmatrix.h @@ -38,7 +38,7 @@ class RDMatrix BtSrc16=24,Harlond=25,Acu1p=26,LiveWireMcastGpio=27,Am16=28, LiveWireLwrpGpio=29,BtSentinel4Web=30,BtGpi16=31,ModemLines=32, SoftwareAuthority=33,Sas16000=34,RossNkScp=35,BtAdms4422=36, - BtSs41Mlr=37,Modbus=38,LastType=39}; + BtSs41Mlr=37,Modbus=38,KernelGpio=39,LastType=40}; enum Endpoint {Input=0,Output=1}; enum Mode {Stereo=0,Left=1,Right=2}; enum VguestAttribute {VguestEngine=0,VguestDevice=1,VguestSurface=2, diff --git a/ripcd/Makefile.am b/ripcd/Makefile.am index 188132fb..df5610a8 100644 --- a/ripcd/Makefile.am +++ b/ripcd/Makefile.am @@ -50,6 +50,7 @@ dist_ripcd_SOURCES = acu1p.cpp acu1p.h\ btsrc16.cpp btsrc16.h\ btsrc8iii.cpp btsrc8iii.h\ harlond.cpp harlond.h\ + kernelgpio.cpp kernelgpio.h\ livewire_lwrpaudio.cpp livewire_lwrpaudio.h\ livewire_lwrpgpio.cpp livewire_lwrpgpio.h\ livewire_mcastgpio.cpp livewire_mcastgpio.h\ @@ -96,6 +97,7 @@ nodist_ripcd_SOURCES = moc_am16.cpp\ moc_btss44.cpp\ moc_btss82.cpp\ moc_harlond.cpp\ + moc_kernelgpio.cpp\ moc_livewire_lwrpaudio.cpp\ moc_livewire_lwrpgpio.cpp\ moc_livewire_mcastgpio.cpp\ diff --git a/ripcd/kernelgpio.cpp b/ripcd/kernelgpio.cpp new file mode 100644 index 00000000..6acddebd --- /dev/null +++ b/ripcd/kernelgpio.cpp @@ -0,0 +1,211 @@ +// kernelgpio.cpp +// +// A Rivendell switcher driver for the Kernel GPIO interface. +// +// (C) Copyright 2017 Fred Gleason +// +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License version 2 as +// published by the Free Software Foundation. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public +// License along with this program; if not, write to the Free Software +// Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +// + +#include + +#include + +#include "kernelgpio.h" + +KernelGpio::KernelGpio(RDMatrix *matrix,QObject *parent) + : Switcher(matrix,parent) +{ + printf("HERE\n"); + // + // Get Matrix Parameters + // + gpio_matrix=matrix->matrix(); + gpio_gpis=matrix->gpis(); + gpio_gpos=matrix->gpos(); + + // + // Initialize the interface + // + gpio_reset_mapper=new QSignalMapper(this); + connect(gpio_reset_mapper,SIGNAL(mapped(int)),this,SLOT(gpoResetData(int))); + gpio_gpio=new RDKernelGpio(this); + connect(gpio_gpio,SIGNAL(valueChanged(int,bool)), + this,SLOT(gpiChangedData(int,bool))); + for(int i=0;iaddGpio(i); + gpio_gpio->setDirection(i,RDKernelGpio::In); + gpio_gpi_mask.push_back(false); + } + for(int i=0;iaddGpio(gpio_gpis+i); + gpio_gpio->setDirection(gpio_gpis+i,RDKernelGpio::Out); + gpio_gpi_mask.push_back(false); + gpio_reset_states.push_back(false); + gpio_reset_timers.push_back(new QTimer(this)); + connect(gpio_reset_timers.back(),SIGNAL(timeout()), + gpio_reset_mapper,SLOT(map())); + gpio_reset_mapper->setMapping(gpio_reset_timers.back(),i); + } + + // + // Interval OneShot + // + gpio_gpi_oneshot=new RDOneShot(this); + connect(gpio_gpi_oneshot,SIGNAL(timeout(int)),this,SLOT(gpiOneshotData(int))); +} + + +KernelGpio::~KernelGpio() +{ + for(unsigned i=0;icommand()) { + case RDMacro::GO: + if((cmd->argQuantity()!=5)|| + ((cmd->arg(1).toString().lower()!="i")&& + (cmd->arg(1).toString().lower()!="o"))|| + (cmd->arg(2).toInt()<1)||(cmd->arg(2).toInt()>gpio_gpos)|| + ((cmd->arg(3).toInt()!=1)&&(cmd->arg(3).toInt()!=0)&& + (cmd->arg(3).toInt()!=-1))||(cmd->arg(4).toInt()<0)) { + cmd->acknowledge(false); + emit rmlEcho(cmd); + return; + } + if(cmd->arg(1).toString().lower()=="i") { + if(cmd->arg(3).toInt()==0) { + emit gpiChanged(gpio_matrix,cmd->arg(2).toInt()-1,false); + gpio_gpi_mask[cmd->arg(2).toInt()-1]=true; + if(cmd->arg(4).toInt()>0) { + gpio_gpi_oneshot-> + start(cmd->arg(2).toInt()-1,cmd->arg(4).toInt()); + } + } + else { + if(cmd->arg(3).toInt()==1) { + emit gpiChanged(gpio_matrix,cmd->arg(2).toInt()-1,true); + gpio_gpi_mask[cmd->arg(2).toInt()-1]=true; + if(cmd->arg(4).toInt()>0) { + gpio_gpi_oneshot-> + start(cmd->arg(2).toInt()-1,cmd->arg(4).toInt()); + } + } + else { + gpiOneshotData(cmd->arg(2).toInt()-1); + } + } + cmd->acknowledge(true); + emit rmlEcho(cmd); + return; + } + if(cmd->arg(1).toString().lower()=="o") { + if(cmd->arg(3).toInt()==0) { + gpio_gpio->setValue(gpio_gpis+cmd->arg(2).toInt()-1,false); + if(cmd->arg(4).toInt()>0) { + gpio_reset_states[cmd->arg(2).toInt()-1]=true; + gpio_reset_timers[cmd->arg(2).toInt()-1]-> + start(cmd->arg(4).toInt(),true); + } + } + else { + if(cmd->arg(3).toInt()==1) { + gpio_gpio->setValue(gpio_gpis+cmd->arg(2).toInt()-1,true); + if(cmd->arg(4).toInt()>0) { + gpio_reset_states[cmd->arg(2).toInt()-1]=false; + gpio_reset_timers[cmd->arg(2).toInt()-1]-> + start(cmd->arg(4).toInt(),true); + } + } + else { + cmd->acknowledge(false); + emit rmlEcho(cmd); + return; + } + } + cmd->acknowledge(true); + emit rmlEcho(cmd); + return; + } + break; + + default: + cmd->acknowledge(false); + emit rmlEcho(cmd); + break; + } +} + + +void KernelGpio::gpiChangedData(int line,bool state) +{ + if(linevalue(value)); +} + + +void KernelGpio::gpoResetData(int line) +{ + gpio_gpio->setValue(gpio_gpis+line,gpio_reset_states[line]); + emit gpoChanged(gpio_matrix,line,gpio_reset_states[line]); +} diff --git a/ripcd/kernelgpio.h b/ripcd/kernelgpio.h new file mode 100644 index 00000000..233f8fcf --- /dev/null +++ b/ripcd/kernelgpio.h @@ -0,0 +1,69 @@ +// kernelgpio.h +// +// A Rivendell switcher driver for the Kernel GPIO interface. +// +// (C) Copyright 2017 Fred Gleason +// +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License version 2 as +// published by the Free Software Foundation. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public +// License along with this program; if not, write to the Free Software +// Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +// + +#ifndef KERNELGPIO_H +#define KERNELGPIO_H + +#include + +#include + +#include +#include +#include +#include +#include + +#include + +class KernelGpio : public Switcher +{ + Q_OBJECT + public: + KernelGpio(RDMatrix *matrix,QObject *parent=0); + ~KernelGpio(); + RDMatrix::Type type(); + unsigned gpiQuantity(); + unsigned gpoQuantity(); + bool primaryTtyActive(); + bool secondaryTtyActive(); + void processCommand(RDMacro *cmd); + + private slots: + void gpiChangedData(int line,bool state); + // void gpoChangedData(int line,bool state); + void gpiOneshotData(int value); + void gpoResetData(int line); + + private: + RDKernelGpio *gpio_gpio; + RDOneShot *gpio_gpi_oneshot; + int gpio_matrix; + int gpio_gpis; + int gpio_gpos; + bool gpio_open; + std::vector gpio_gpi_mask; + QSignalMapper *gpio_reset_mapper; + std::vector gpio_reset_timers; + std::vector gpio_reset_states; +}; + + +#endif // KERNELGPIO_H diff --git a/ripcd/loaddrivers.cpp b/ripcd/loaddrivers.cpp index 7a46f6e7..3a48e96d 100644 --- a/ripcd/loaddrivers.cpp +++ b/ripcd/loaddrivers.cpp @@ -42,6 +42,7 @@ #include #include #include +#include #include #include #include @@ -142,6 +143,10 @@ bool MainObject::LoadSwitchDriver(int matrix_num) ripcd_switcher[matrix_num]=new Harlond(matrix,this); break; + case RDMatrix::KernelGpio: + ripcd_switcher[matrix_num]=new KernelGpio(matrix,this); + break; + case RDMatrix::LiveWireLwrpAudio: ripcd_switcher[matrix_num]=new LiveWireLwrpAudio(matrix,this); break;