Rivendellaudio/lib/rdgpio.cpp
Fred Gleason 698b475933 2016-05-24 Fred Gleason <fredg@paravelsystems.com>
* Removed all CVS tags.
	* Removed 'const char *name' parameter from all QObject contructors.
2016-05-24 13:13:26 -04:00

432 lines
7.8 KiB
C++

// rdgpio.cpp
//
// A driver for General-Purpose I/O devices.
//
// (C) Copyright 2002-2003,2016 Fred Gleason <fredg@paravelsystems.com>
//
// 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 <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <sys/ioctl.h>
#include <unistd.h>
#include <qobject.h>
#include <rdgpio.h>
RDGpio::RDGpio(QObject *parent)
: QObject(parent)
{
Clear();
//
// Input Timer
//
gpio_input_timer=new QTimer(this,"input_timer");
connect(gpio_input_timer,SIGNAL(timeout()),this,SLOT(inputTimerData()));
gpio_revert_mapper=NULL;
for(int i=0;i<GPIO_MAX_LINES;i++) {
gpio_revert_timer[i]=NULL;
}
for(int i=0;i<KEY_MAX;i++) {
gpio_key_map[i]=-1;
}
}
QString RDGpio::device() const
{
return gpio_device;
}
void RDGpio::setDevice(QString dev)
{
gpio_device=dev;
}
QString RDGpio::description() const
{
return gpio_description;
}
RDGpio::Mode RDGpio::mode()
{
struct gpio_info info;
switch(gpio_api) {
case RDGpio::ApiGpio:
ioctl(gpio_fd,GPIO_GETINFO,&info);
return (RDGpio::Mode)info.mode;
case RDGpio::ApiInput:
return RDGpio::Auto;
}
return RDGpio::Auto;
}
void RDGpio::setMode(RDGpio::Mode mode)
{
unsigned umode=(unsigned)mode;
switch(gpio_api) {
case RDGpio::ApiGpio:
ioctl(gpio_fd,GPIO_SETMODE,&umode);
RemapTimers();
break;
default:
break;
}
}
bool RDGpio::open()
{
int ver;
if(gpio_open) {
return false;
}
if((gpio_fd=::open((const char *)gpio_device,O_RDONLY|O_NONBLOCK))<0) {
return false;
}
if(ioctl(gpio_fd,GPIO_GETINFO,&gpio_info)==0) {
gpio_api=RDGpio::ApiGpio;
InitGpio();
RemapTimers();
}
else {
if(ioctl(gpio_fd,EVIOCGVERSION,&ver)==0) {
gpio_api=RDGpio::ApiInput;
InitInput();
}
else {
::close(gpio_fd);
return false;
}
}
gpio_open=true;
gpio_input_timer->start(GPIO_CLOCK_INTERVAL);
return true;
}
void RDGpio::close()
{
if(!gpio_open) {
return;
}
gpio_input_timer->stop();
::close(gpio_fd);
gpio_open=false;
if(gpio_revert_mapper!=NULL) {
delete gpio_revert_mapper;
gpio_revert_mapper=NULL;
}
for(int i=0;i<outputs();i++) {
if(gpio_revert_timer[i]!=NULL) {
delete gpio_revert_timer[i];
gpio_revert_timer[i]=NULL;
}
}
}
int RDGpio::inputs() const
{
if(!gpio_open) {
return 0;
}
return gpio_info.inputs;
}
int RDGpio::outputs() const
{
if(!gpio_open) {
return 0;
}
return gpio_info.outputs;
}
unsigned RDGpio::inputMask()
{
struct gpio_mask mask;
struct input_event input;
static unsigned input_mask=0;
if(!gpio_open) {
return 0;
}
switch(gpio_api) {
case RDGpio::ApiGpio:
memset(&mask,0,sizeof(struct gpio_mask));
ioctl(gpio_fd,GPIO_GET_INPUTS,&mask);
return mask.mask[0];
case RDGpio::Input:
while(read(gpio_fd,&input,sizeof(input))>0) {
if((input.type==EV_KEY)&&(gpio_key_map[input.code]>=0)) {
if(input.value==0) {
input_mask&=~(1<<gpio_key_map[input.code]);
}
else {
input_mask|=(1<<gpio_key_map[input.code]);
}
}
}
return input_mask;
}
return 0;
}
bool RDGpio::inputState(int line)
{
if(!gpio_open) {
return false;
}
if((inputMask()&(1<<line))==0) {
return false;
}
return true;
}
unsigned RDGpio::outputMask() const
{
struct gpio_mask mask;
if(!gpio_open) {
return 0;
}
switch(gpio_api) {
case RDGpio::ApiGpio:
memset(&mask,0,sizeof(struct gpio_mask));
ioctl(gpio_fd,GPIO_GET_OUTPUTS,&mask);
return mask.mask[0];
case RDGpio::ApiInput:
return 0;
}
return 0;
}
void RDGpio::gpoSet(int line,unsigned interval)
{
struct gpio_line out;
if(!gpio_open) {
return;
}
switch(gpio_api) {
case RDGpio::ApiGpio:
out.line=line;
out.state=1;
ioctl(gpio_fd,GPIO_SET_OUTPUT,&out);
SetReversion(line,interval);
break;
case RDGpio::ApiInput:
break;
}
}
void RDGpio::gpoReset(int line,unsigned interval)
{
struct gpio_line out;
if(!gpio_open) {
return;
}
switch(gpio_api) {
case RDGpio::ApiGpio:
out.line=line;
out.state=0;
ioctl(gpio_fd,GPIO_SET_OUTPUT,&out);
SetReversion(line,interval);
break;
case RDGpio::ApiInput:
break;
}
}
void RDGpio::inputTimerData()
{
unsigned input_mask;
unsigned output_mask;
unsigned mask;
if((input_mask=inputMask())!=gpio_input_mask) {
for(int i=0;i<inputs();i++) {
mask=1<<i;
if((gpio_input_mask&mask)!=(input_mask&mask)) {
if((input_mask&mask)==0) {
emit inputChanged(i,false);
}
else {
emit inputChanged(i,true);
}
}
}
gpio_input_mask=input_mask;
}
if((output_mask=outputMask())!=gpio_output_mask) {
for(int i=0;i<outputs();i++) {
mask=1<<i;
if((gpio_output_mask&mask)!=(output_mask&mask)) {
if((output_mask&mask)==0) {
emit outputChanged(i,false);
}
else {
emit outputChanged(i,true);
}
}
}
gpio_output_mask=output_mask;
}
}
void RDGpio::revertData(int id)
{
if((outputMask()&(1<<id))==0) {
gpoSet(id);
}
else {
gpoReset(id);
}
}
void RDGpio::RemapTimers()
{
struct gpio_info info;
//
// Free Old Timers
//
if(gpio_revert_mapper!=NULL) {
delete gpio_revert_mapper;
gpio_revert_mapper=NULL;
}
for(int i=0;i<gpio_info.outputs;i++) {
if(gpio_revert_timer[i]!=NULL) {
delete gpio_revert_timer[i];
gpio_revert_timer[i]=NULL;
}
}
//
// Create New Timers
//
ioctl(gpio_fd,GPIO_GETINFO,&info);
gpio_revert_mapper=new QSignalMapper(this,"gpio_revert_mapper");
connect(gpio_revert_mapper,SIGNAL(mapped(int)),this,SLOT(revertData(int)));
for(int i=0;i<info.outputs;i++) {
gpio_revert_timer[i]=new QTimer(this);
gpio_revert_mapper->setMapping(gpio_revert_timer[i],i);
connect(gpio_revert_timer[i],SIGNAL(timeout()),
gpio_revert_mapper,SLOT(map()));
}
}
void RDGpio::SetReversion(int line,unsigned interval)
{
if(interval==0) {
if(gpio_revert_timer[line]->isActive()) {
gpio_revert_timer[line]->stop();
}
return;
}
if(gpio_revert_timer[line]->isActive()) {
gpio_revert_timer[line]->changeInterval(interval);
}
else {
gpio_revert_timer[line]->start(interval,true);
}
}
void RDGpio::Clear()
{
gpio_open=false;
gpio_description="Unknown Device";
memset(&gpio_info,0,sizeof(struct gpio_info));
gpio_input_mask=0;
gpio_api=RDGpio::ApiGpio;
for(int i=0;i<KEY_MAX;i++) {
gpio_key_map[i]=-1;
}
}
void RDGpio::InitGpio()
{
gpio_description=gpio_info.name;
//
// Enable Input Filters
//
if((gpio_info.caps&GPIO_CAP_FILTER)!=0) {
gpio_mask mask;
memset(&mask,0xFF,sizeof(struct gpio_mask));
ioctl(gpio_fd,GPIO_SET_FILTERS,&mask);
}
}
void RDGpio::InitInput()
{
unsigned char bitmask[EV_MAX/8+1];
unsigned char keymask[KEY_MAX/8+1];
int i=0;
char desc[256];
memset(&gpio_info,0,sizeof(struct gpio_info));
if(ioctl(gpio_fd,EVIOCGNAME(256),desc)>=0) {
gpio_description=desc;
}
if(ioctl(gpio_fd,EVIOCGBIT(0,EV_MAX),bitmask)<0) {
return;
}
if((bitmask[0]&(1<<EV_KEY))==0) {
return;
}
ioctl(gpio_fd,EVIOCGBIT(EV_KEY,KEY_MAX),keymask);
while((i<KEY_MAX)&&(gpio_info.inputs<GPIO_MAX_LINES)) {
if((keymask[i/8]&(1<<(i%8)))!=0) {
gpio_key_map[i]=gpio_info.inputs++;
}
i++;
}
gpio_info.mode=RDGpio::Auto;
gpio_info.outputs=0;
}