You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
ktechlab/src/electronics/port.cpp

515 lines
9.1 KiB

/***************************************************************************
* Copyright (C) 2005 by David Saxton *
* david@bluehaze.org *
* *
* This program is free software; you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation; either version 2 of the License, or *
* (at your option) any later version. *
***************************************************************************/
#include "port.h"
#include <kdebug.h>
#include <errno.h>
#include <fcntl.h>
#include <linux/ppdev.h>
#include <sys/ioctl.h>
#include <unistd.h>
//BEGIN class Port
Port::Port()
{
}
Port::~Port()
{
}
TQStringList Port::ports( unsigned probeResult )
{
return SerialPort::ports(probeResult) + ParallelPort::ports(probeResult);
}
//END class Port
//BEGIN class SerialPort
SerialPort::SerialPort()
{
m_file = -1;
}
SerialPort::~SerialPort()
{
closePort();
}
void SerialPort::setPinState( Pin pin, bool state )
{
if ( m_file == -1 )
return;
int flags = -1;
switch ( pin )
{
case TD:
ioctl( m_file, state ? TIOCSBRK : TIOCCBRK, 0 );
return;
case DTR:
flags = TIOCM_DTR;
break;
case DSR:
flags = TIOCM_DSR;
break;
case RTS:
flags = TIOCM_RTS;
break;
case CD:
case RD:
case GND:
case CTS:
case RI:
break;
};
if ( flags == -1 )
{
kdError() << k_funcinfo << "Bad pin " << pin << endl;
return;
}
if ( ioctl( m_file, state ? TIOCMBIS : TIOCMBIC, & flags ) == -1 )
kdError() << k_funcinfo << "Could not set pin " << pin << " errno = " << errno << endl;
}
bool SerialPort::pinState( Pin pin )
{
if ( m_file == -1 )
return false;
int mask = 0;
switch ( pin )
{
case CD:
mask = TIOCM_CD;
break;
case RD:
mask = TIOCM_SR;
break;
case CTS:
mask = TIOCM_CTS;
break;
case RI:
mask = TIOCM_RI;
break;
case TD:
case DTR:
case GND:
case DSR:
case RTS:
break;
}
if ( mask == 0 )
{
kdError() << k_funcinfo << "Bad pin " << pin << endl;
return false;
}
int bits = 0;
if ( ioctl( m_file, TIOCMGET, & bits ) == -1 )
{
kdError() << k_funcinfo << "Could not read pin" << pin << " errno = " << errno << endl;
return false;
}
return bits & mask;
}
Port::ProbeResult SerialPort::probe( const TQString & port )
{
int file = open( port.ascii(), O_NOCTTY | O_NONBLOCK | O_RDONLY );
if ( file == -1 )
return Port::DoesntExist;
close(file);
file = open( port.ascii(), O_NOCTTY | O_NONBLOCK | O_RDWR );
if ( file == -1 )
return Port::ExistsButNotRW;
close(file);
return Port::ExistsAndRW;
}
bool SerialPort::openPort( const TQString & port, speed_t baudRate )
{
closePort();
m_file = open( port.ascii(), O_NOCTTY | O_NONBLOCK | O_RDWR );
if ( m_file == -1 )
{
kdError() << k_funcinfo << "Could not open port " << port << endl;
return false;
}
termios state;
tcgetattr( m_file, & state );
// Save the previous state for restoration in close.
m_previousState = state;
state.c_iflag = IGNBRK | IGNPAR;
state.c_oflag = 0;
state.c_cflag = baudRate | CS8 | CREAD | CLOCAL;
state.c_lflag = 0;
tcsetattr( m_file, TCSANOW, & state );
return true;
}
void SerialPort::closePort()
{
if ( m_file == -1 )
return;
ioctl( m_file, TIOCCBRK, 0 );
usleep(1);
tcsetattr( m_file, TCSANOW, & m_previousState );
close( m_file );
m_file = -1;
}
TQStringList SerialPort::ports( unsigned probeResult )
{
TQStringList list;
for ( int i = 0; i < 8; ++i )
{
TQString dev = TQString("/dev/ttyS%1").arg(i);
if ( probe(dev) & probeResult )
list << dev;
}
for ( unsigned i = 0; i < 8; ++i )
{
TQString dev = TQString("/dev/tts/%1").arg(i);
if ( probe(dev) & probeResult )
list << dev;
}
for ( unsigned i = 0; i < 8; ++i )
{
TQString dev = TQString("/dev/ttyUSB%1").arg(i);
if ( probe(dev) & probeResult )
list << dev;
}
for ( unsigned i = 0; i < 8; ++i )
{
TQString dev = TQString("/dev/usb/tts/%1").arg(i);
if ( probe(dev) & probeResult )
list << dev;
}
return list;
}
//END class SerialPort
//BEGIN class ParallelPort
const int IRQ_MODE_BIT = 1 << 20; // Controls if pin 10 (Ack) causes interrupts
const int INPUT_MODE_BIT = 1 << 21; // Controls if the data pins are input or output
const int ALWAYS_INPUT_PINS = ParallelPort::STATUS_PINS;
const unsigned int IOCTL_REG_READ[3] = {
PPRDATA,
PPRSTATUS,
PPRCONTROL,
};
const unsigned int IOCTL_REG_WRITE[3] = {
PPWDATA,
0,
PPWCONTROL,
};
const int INVERT_MASK[3] = {
0x0,
0x80, // 10000000
0x0b, // 00001011
};
ParallelPort::ParallelPort()
{
reset();
}
ParallelPort::~ParallelPort()
{
}
void ParallelPort::reset()
{
m_file = -1;
m_reg[Data] = 0;
m_reg[Status] = 0;
m_reg[Control] = 0;
m_outputPins = INPUT_MODE_BIT | IRQ_MODE_BIT;
m_inputPins = ALWAYS_INPUT_PINS | INPUT_MODE_BIT | IRQ_MODE_BIT;
}
//BEGIN Pin-oriented operations
void ParallelPort::setPinState( int pins, bool state )
{
// only allow writing to output pins
pins &= m_outputPins;
if ( pins & DATA_PINS )
setDataState( (pins & DATA_PINS) >> 0, state );
if ( pins & CONTROL_PINS )
setControlState( (pins & CONTROL_PINS) >> 16, state );
}
int ParallelPort::pinState( int pins )
{
int value = 0;
// only allow reading from input pins
pins &= m_inputPins;
if ( pins & DATA_PINS )
value |= ((readFromRegister( Data ) & ((pins & DATA_PINS) >> 0)) << 0);
if ( pins & STATUS_PINS )
value |= ((readFromRegister( Status ) & ((pins & STATUS_PINS) >> 8)) << 8);
if ( pins & CONTROL_PINS )
value |= ((readFromRegister( Control ) & ((pins & CONTROL_PINS) >> 16)) << 16);
return value;
}
void ParallelPort::setDataState( uchar pins, bool state )
{
uchar value = readFromRegister( Data );
if ( state )
value |= pins;
else
value &= ~pins;
writeToData( value );
}
void ParallelPort::setControlState( uchar pins, bool state )
{
uchar value = readFromRegister( Control );
if ( state )
value |= pins;
else
value &= ~pins;
writeToControl( value );
}
//END Pin-oriented operations
//BEGIN Register-oriented operations
uchar ParallelPort::readFromRegister( Register reg )
{
if ( m_file == -1 )
return 0;
// uchar value = inb( m_lpBase + reg ) ^ INVERT_MASK[reg];
uchar value = 0;
if ( ioctl( m_file, IOCTL_REG_READ[reg], &value ) )
kdError() << k_funcinfo << "errno=" << errno << endl;
else
m_reg[reg] = value;
return value;
}
void ParallelPort::writeToRegister( Register reg, uchar value )
{
if ( m_file == -1 )
return;
// outb( value ^ INVERT_MASK[reg], m_lpBase + reg );
if ( ioctl( m_file, IOCTL_REG_WRITE[reg], & value ) )
kdError() << k_funcinfo << "errno=" << errno << endl;
else
m_reg[reg] = value;
}
void ParallelPort::writeToData( uchar value )
{
writeToRegister( Data, value );
}
void ParallelPort::writeToControl( uchar value )
{
// Set all inputs to ones
value |= ((m_inputPins & CONTROL_PINS) >> 16);
writeToRegister( Control, value );
}
//END Register-oriented operations
//BEGIN Changing pin directions
void ParallelPort::setDataDirection( Direction dir )
{
if ( dir == Input )
{
m_inputPins |= DATA_PINS;
m_outputPins &= ~DATA_PINS;
}
else
{
m_inputPins &= DATA_PINS;
m_outputPins |= ~DATA_PINS;
}
setPinState( INPUT_MODE_BIT, dir == Input );
}
void ParallelPort::setControlDirection( int pins, Direction dir )
{
pins &= CONTROL_PINS;
if ( dir == Input )
{
m_inputPins |= pins;
m_outputPins &= ~pins;
}
else
{
m_inputPins &= pins;
m_outputPins |= ~pins;
}
setControlState( 0, true );
}
//END Changing pin directions
Port::ProbeResult ParallelPort::probe( const TQString & port )
{
int file = open( port.ascii(), O_RDWR );
if ( file == -1 )
return Port::DoesntExist;
if ( ioctl( file, PPCLAIM ) != 0 )
{
close(file);
return Port::ExistsButNotRW;
}
ioctl( file, PPRELEASE );
close(file);
return Port::ExistsAndRW;
}
TQStringList ParallelPort::ports( unsigned probeResult )
{
TQStringList list;
for ( unsigned i = 0; i < 8; ++i )
{
TQString dev = TQString("/dev/parport%1").arg(i);
if ( probe(dev) & probeResult )
list << dev;
}
for ( unsigned i = 0; i < 8; ++i )
{
TQString dev = TQString("/dev/parports/%1").arg(i);
if ( probe(dev) & probeResult )
list << dev;
}
return list;
}
bool ParallelPort::openPort( const TQString & port )
{
if ( m_file != -1 )
{
kdWarning() << k_funcinfo << "Port already open" << endl;
return false;
}
m_file = open( port.ascii(), O_RDWR );
if ( m_file == -1 )
{
kdError() << k_funcinfo << "Could not open port \"" << port << "\": errno="<<errno<<endl;
return false;
}
if ( ioctl( m_file, PPCLAIM ) )
{
kdError() << k_funcinfo << "Port " << port << " must be RW" << endl;
close( m_file );
m_file = -1;
return false;
}
return true;
}
void ParallelPort::closePort()
{
if ( m_file == -1 )
return;
int res = ioctl( m_file, PPRELEASE );
close( m_file );
if ( res )
kdError() << k_funcinfo << "res="<<res<<endl;
m_file = -1;
}
//END class ParallelPort