summaryrefslogtreecommitdiff
path: root/noncore/settings/networksettings2/opietooth2/OTGateway.cpp
Side-by-side diff
Diffstat (limited to 'noncore/settings/networksettings2/opietooth2/OTGateway.cpp') (more/less context) (ignore whitespace changes)
-rw-r--r--noncore/settings/networksettings2/opietooth2/OTGateway.cpp773
1 files changed, 773 insertions, 0 deletions
diff --git a/noncore/settings/networksettings2/opietooth2/OTGateway.cpp b/noncore/settings/networksettings2/opietooth2/OTGateway.cpp
new file mode 100644
index 0000000..2d13ce9
--- a/dev/null
+++ b/noncore/settings/networksettings2/opietooth2/OTGateway.cpp
@@ -0,0 +1,773 @@
+#include <qmessagebox.h>
+#include <qfile.h>
+#include <qdir.h>
+#include <qtextstream.h>
+#include <qpixmap.h>
+#include <qvector.h>
+#include <qpe/resource.h>
+
+#include <opie2/odebug.h>
+
+#include <bluezlib.h>
+
+#include <OTDevice.h>
+#include <OTDriver.h>
+#include <OTInquiry.h>
+#include <OTDriverList.h>
+#include <OTDeviceAddress.h>
+#include <OTGateway.h>
+
+using namespace Opietooth2;
+
+// single instance
+OTGateway * OTGateway::SingleGateway = 0;
+int OTGateway::UseCount = 0;
+
+OTGateway * OTGateway::getOTGateway( void ) {
+ if(SingleGateway == 0 ) {
+ SingleGateway = new OTGateway();
+ }
+
+ UseCount ++;
+ return SingleGateway;
+}
+
+void OTGateway::releaseOTGateway( void ) {
+ UseCount --;
+ if( UseCount == 0 ) {
+ delete SingleGateway;
+ SingleGateway = 0;
+ }
+}
+
+// open bluetooth system
+OTGateway::OTGateway( void ) : QObject( 0, "OTGateway" ),
+ AllDrivers( this ),
+ AllPeers() {
+
+ ErrorConnectCount = 0;
+ TheOTDevice = 0;
+ Scanning = 0;
+ AllPeersModified = 0;
+ AllPeers.setAutoDelete( TRUE );
+
+ if ( ( HciCtl = socket(AF_BLUETOOTH, SOCK_RAW, BTPROTO_HCI)) < 0) {
+ SLOT_ShowError( tr( "error opening hci socket" ) );
+ return;
+ }
+
+ // load all known devices
+ updateDrivers();
+
+ // load all peers we have ever seen
+ loadKnownPeers();
+
+ // iterate over drivers and find active connections
+ // adding/updating peers
+ loadActiveConnections();
+
+ // check every 4 seconds the state of BT
+ timerEvent(0);
+ RefreshTimer = -1;
+ setRefreshTimer( 4000 );
+
+ // load known link keys
+ readLinkKeys();
+}
+
+// close bluetooth system
+OTGateway::~OTGateway( void ) {
+
+ if( AllPeersModified ) {
+ saveKnownPeers();
+ }
+
+ if( Scanning )
+ delete Scanning;
+
+ if( TheOTDevice )
+ delete TheOTDevice;
+
+ if( HciCtl >= 0 ) {
+ ::close( HciCtl );
+ }
+}
+
+void OTGateway::setRefreshTimer( int T ) {
+ if( RefreshTimer != -1 ) {
+ killTimer( RefreshTimer );
+ }
+
+ if( T == 0 )
+ T = 4000;
+ RefreshTimer = startTimer( T );
+}
+
+OTDevice * OTGateway::getOTDevice( ) {
+ if( TheOTDevice == 0 ) {
+ // load bluetooth device and check state
+ TheOTDevice = new OTDevice( this );
+ connect( TheOTDevice,
+ SIGNAL( isEnabled( int, bool ) ),
+ this,
+ SLOT( SLOT_Enabled( int, bool ) ) );
+
+ connect( TheOTDevice,
+ SIGNAL( error( const QString & ) ),
+ this,
+ SLOT( SLOT_ShowError( const QString & ) ) );
+ }
+
+ return TheOTDevice;
+}
+
+// start bluetooth (if stopped)
+// return TRUE if started
+void OTGateway::SLOT_SetEnabled( bool Mode ) {
+ if( Mode ) {
+ SLOT_Enable();
+ return;
+ }
+ SLOT_Disable();
+}
+
+void OTGateway::SLOT_Enable() {
+ getOTDevice()->attach();
+}
+
+void OTGateway::SLOT_Disable() {
+ getOTDevice()->detach();
+}
+
+bool OTGateway::needsEnabling() {
+ return getOTDevice()->needsAttach();
+}
+
+bool OTGateway::isEnabled() {
+ if( getOTDevice()->deviceNr() >= 0 &&
+ driver( getOTDevice()->deviceNr() )->isUp() )
+ return TRUE;
+
+ // else check system
+ return getOTDevice()->isAttached();
+}
+
+void OTGateway::SLOT_ShowError( const QString & S ) {
+
+ owarn << S << oendl;
+
+ if( ErrorConnectCount > 0 ) {
+ // pass error
+ emit error( QString( "<p>" ) + S + "</p>" );
+ return;
+ }
+
+ QMessageBox::warning( 0,
+ tr("OTGateway error"),
+ S );
+}
+
+void OTGateway::connectNotify( const char * S ) {
+ if( strcmp( S, "error(const QString&)" ) == 0 ) {
+ ErrorConnectCount ++;
+ }
+}
+
+void OTGateway::disconnectNotify( const char * S ) {
+ if( strcmp( S, "error(const QString&)" ) == 0 ) {
+ ErrorConnectCount --;
+ }
+}
+
+void OTGateway::timerEvent( QTimerEvent * ) {
+
+ OTDriver * D;
+ unsigned int oldc = AllDrivers.count();
+ bool old;
+
+ AllDrivers.update();
+
+ if( oldc != AllDrivers.count() ) {
+ updateDrivers();
+ } else {
+ for( unsigned int i = 0;
+ i < AllDrivers.count();
+ i ++ ) {
+ D = AllDrivers[i];
+ old = D->isUp();
+ if( D->currentState() >= 0 ) {
+ if( old != D->isUp() ) {
+ emit stateChange( D, D->isUp() );
+ }
+ } else {
+ // if one driver is unable to provide info
+ // we refresh all devices
+ updateDrivers();
+ return;
+ }
+ }
+ }
+}
+
+void OTGateway::SLOT_Enabled( int id, bool Up ) {
+ owarn << "device " << id << " state " << Up << oendl;
+ if( Up ) {
+ // device is up -> detect it
+ updateDrivers();
+ if( (unsigned)id >= AllDrivers.count() ) {
+ // to make sure that the driver really IS detected
+ AllDrivers[id]->bringUp();
+ }
+ } // if DOWN device already down
+ emit deviceEnabled( Up );
+}
+
+void OTGateway::updateDrivers( void ) {
+ OTDriver * D;
+
+ AllDrivers.update();
+
+ owarn << "updated drivers. now " << AllDrivers.count() << oendl;
+
+ // connect signals for each driver
+ for( unsigned int i = 0;
+ i < AllDrivers.count();
+ i ++ ) {
+ D = AllDrivers[i];
+
+ connect( D,
+ SIGNAL( error( const QString & ) ),
+ this,
+ SLOT( SLOT_ShowError( const QString & ) )
+ );
+
+ connect( D,
+ SIGNAL( stateChange( OTDriver *, bool ) ),
+ this,
+ SIGNAL( stateChange( OTDriver *, bool ) )
+ );
+
+ connect( D,
+ SIGNAL( driverDisappeared( OTDriver * ) ),
+ this,
+ SLOT( SLOT_DriverDisappeared( OTDriver * ) )
+ );
+ }
+
+ // verify main device too
+ if( TheOTDevice )
+ TheOTDevice->checkAttach();
+
+ // set to default scanning hardware
+ setScanWith( 0 );
+
+ emit driverListChanged();
+}
+
+void OTGateway::SLOT_DriverDisappeared( OTDriver * D ) {
+ owarn << "Driver " << D->devname() << " when offline" << oendl;
+ updateDrivers();
+}
+
+void OTGateway::scanNeighbourhood( OTDriver * D ) {
+
+ if( Scanning ) {
+ stopScanOfNeighbourhood();
+ }
+
+ if( D ) {
+ setScanWith( D );
+ }
+
+ Scanning = new OTInquiry( scanWith() );
+
+ connect( Scanning,
+ SIGNAL( peerFound( OTPeer *, bool )),
+ this,
+ SLOT( SLOT_PeerDetected( OTPeer *, bool ) )
+ );
+ connect( Scanning,
+ SIGNAL( finished()),
+ this,
+ SLOT( SLOT_FinishedDetecting() )
+ );
+
+ // start scanning
+ Scanning->inquire( 30.0 );
+}
+
+OTPeer* OTGateway::findPeer( const OTDeviceAddress & Addr ) {
+ for( unsigned int i = 0 ; i < AllPeers.count(); i ++ ) {
+ if( AllPeers[i]->address() == Addr ) {
+ return AllPeers[i];
+ }
+ }
+ return 0;
+}
+
+OTDriver* OTGateway::findDriver( const OTDeviceAddress & Addr ) {
+ for( unsigned int i = 0 ; i < AllDrivers.count(); i ++ ) {
+ if( AllDrivers[i]->address() == Addr ) {
+ return AllDrivers[i];
+ }
+ }
+ return 0;
+}
+
+void OTGateway::SLOT_PeerDetected( OTPeer * P, bool IsNew ) {
+
+ if( IsNew ) {
+ // new peer
+ owarn << "New peer " << P->name() << oendl;
+ addPeer( P );
+ }
+
+ emit detectedPeer( P, IsNew );
+}
+
+void OTGateway::addPeer( OTPeer * P ) {
+ AllPeers.resize( AllPeers.size()+1);
+ AllPeers.insert( AllPeers.size()-1, P );
+ AllPeersModified = 1;
+}
+
+void OTGateway::removePeer( OTPeer * P ) {
+ int i = AllPeers.find( P );
+ if( i ) {
+ AllPeers.remove( i );
+ AllPeersModified = 1;
+ }
+}
+
+void OTGateway::stopScanOfNeighbourhood( void ) {
+ if( Scanning ) {
+ delete Scanning;
+ Scanning = 0;
+ }
+}
+
+void OTGateway::SLOT_FinishedDetecting() {
+ stopScanOfNeighbourhood();
+ emit finishedDetecting();
+}
+
+const char * OTGateway::deviceTypeToName( int cls ) {
+ switch ( (cls & 0x001F00) >> 8) {
+ case 0x00:
+ return "misc";
+ case 0x01:
+ return "computer";
+ case 0x02:
+ return "phone";
+ case 0x03:
+ return "lan";
+ case 0x04:
+ return "av";
+ case 0x05:
+ return "peripheral";
+ case 0x06:
+ return "imaging";
+ case 0x07:
+ default :
+ break;
+ }
+ return "unknown";
+}
+
+PANConnectionVector OTGateway::getPANConnections( void ) {
+ PANConnectionVector V;
+
+ struct bnep_connlist_req req;
+ struct bnep_conninfo ci[48];
+
+ V.setAutoDelete(TRUE);
+
+ int ctl = socket(PF_BLUETOOTH, SOCK_RAW, BTPROTO_BNEP);
+ if (ctl < 0) {
+ owarn << "Failed to open control socket" << oendl;
+ return V;
+ }
+
+ req.cnum = 48;
+ req.ci = ci;
+ if (ioctl(ctl, BNEPGETCONNLIST, &req)) {
+ owarn << "Failed to get connection list" << oendl;
+ ::close( ctl );
+ return V;
+ }
+
+ for ( unsigned i=0; i < req.cnum; i++) {
+ V.resize( V.size() + 1 );
+ if( ci[i].role == BNEP_SVC_PANU ) {
+ // we are the client
+ V.insert( V.size()-1, new OTPANConnection(
+ ci[i].device,
+ batostr((bdaddr_t *) ci[i].dst)
+ ) );
+ }
+ }
+
+ ::close( ctl );
+ return V;
+}
+
+struct link_key {
+ bdaddr_t sba;
+ bdaddr_t dba;
+ uint8_t key[16];
+ uint8_t type;
+ time_t time;
+};
+
+void OTGateway::readLinkKeys( void ) {
+
+ struct link_key k;
+ int rv;
+
+ AllKeys.truncate(0);
+
+ QFile F( "/etc/bluetooth/link_key" );
+
+ if( ! F.open( IO_ReadOnly ) ) {
+ emit error( tr("Cannot open link_key file") );
+ return;
+ }
+
+ while( 1 ) {
+ rv = F.readBlock( (char *)&k, sizeof( k ) );
+ if( rv == 0 )
+ // EOF
+ break;
+
+ if( rv < 0 ) {
+ emit error( tr("Read error in link key file") );
+ }
+
+ AllKeys.resize( AllKeys.size()+1 );
+ AllKeys[ AllKeys.size()-1 ].From.setBDAddr( k.sba );
+ AllKeys[ AllKeys.size()-1 ].To.setBDAddr( k.dba );
+ }
+}
+
+bool OTGateway::removeLinkKey( unsigned int Index ) {
+ OTLinkKey & LK = AllKeys[Index];
+
+ struct link_key k;
+ int rv;
+
+ QFile F( "/etc/bluetooth/link_key" );
+ QFile OutF( "/etc/bluetooth/newlink_key" );
+
+ if( ! F.open( IO_ReadOnly ) ) {
+ emit error( tr("Cannot open link_key file") );
+ return 0;
+ }
+
+ if( ! OutF.open( IO_WriteOnly | IO_Truncate ) ) {
+ emit error( tr("Cannot open temporary link_key file") );
+ return 0;
+ }
+
+ while( 1 ) {
+ rv = F.readBlock( (char *)&k, sizeof( k ) );
+ if( rv == 0 )
+ // EOF
+ break;
+
+ if( rv < 0 ) {
+ emit error( tr("Read error in link key file") );
+ return 0;
+ }
+
+ if( LK.from() != OTDeviceAddress( k.sba ) ||
+ LK.to() != OTDeviceAddress( k.dba ) ) {
+ // copy
+ OutF.writeBlock( (char *)&k, sizeof( k ) );
+ } // else remove this key
+ }
+
+ // rename files
+ QDir D( "/etc/bluetooth" );
+
+ D.remove( "link_key" );
+ D.rename( "newlink_key", "link_key" );
+
+ // restart hcid
+ system( "/etc/init.d/hcid stop" );
+ system( "/etc/init.d/hcid start" );
+
+ // remove from table
+ if( Index < (AllKeys.size()-1) ) {
+ // collapse array
+ AllKeys[Index] = AllKeys[AllKeys.size()-1];
+ }
+
+ // remove last element
+ AllKeys.resize( AllKeys.size()-1 );
+
+ return 1;
+}
+
+#define MAXCONNECTIONS 10
+void OTGateway::loadActiveConnections( void ) {
+
+ struct hci_conn_list_req *cl;
+ struct hci_conn_info *ci;
+ OTDeviceAddress Addr;
+ OTPeer * P;
+
+ if (!(cl = (struct hci_conn_list_req *)malloc(
+ MAXCONNECTIONS * sizeof(*ci) + sizeof(*cl)))) {
+ emit error( tr("Can't allocate memory") );
+ return;
+ }
+ memset( cl, 0, MAXCONNECTIONS * sizeof(*ci) + sizeof(*cl) );
+
+ for( unsigned int i = 0;
+ i < AllDrivers.count();
+ i ++ ) {
+
+ if( ! AllDrivers[i]->isUp() ) {
+ continue;
+ }
+
+ // driver is up -> check connections
+ cl->dev_id = AllDrivers[i]->devId();
+ cl->conn_num = MAXCONNECTIONS;
+ ci = cl->conn_info;
+
+ if (ioctl( getSocket(), HCIGETCONNLIST, (void *) cl)) {
+ emit error( tr("Can't get connection list") );
+ break;
+ }
+
+ for ( int k = 0; k < cl->conn_num; k++, ci++) {
+
+ if( ci->state != BT_CONNECTED ) {
+ // not yet connected
+ continue;
+ }
+
+ Addr.setBDAddr( ci->bdaddr );
+ P = findPeer( Addr );
+ if( ! P ) {
+ // peer not yet known -> add
+ P = new OTPeer( this );
+ addPeer( P );
+ P->setAddress( Addr );
+ // infoQueue.push_back(info);
+ P->setName( AllDrivers[i]->getPeerName( Addr ) );
+ }
+ P->setState( OTPeer::Peer_Up );
+ P->setConnectedTo( AllDrivers[i] );
+ }
+ }
+
+ free( cl );
+}
+
+void OTGateway::loadKnownPeers( void ) {
+ QDir SaveDir = QDir::home();
+
+ if( ! SaveDir.exists( "Settings" ) ) {
+ return;
+ }
+ SaveDir.cd( "Settings" );
+
+ if( ! SaveDir.exists( "opietooth" ) ) {
+ return;
+ }
+ SaveDir.cd( "opietooth" );
+
+ QFile F( SaveDir.path() + "/SeenDevices.conf" );
+
+ if( F.open( IO_ReadOnly ) ) {
+ QTextStream TS(&F);
+ long count;
+
+ count = TS.readLine().toLong();
+
+ while( count > 0 ) {
+ addPeer( new OTPeer( TS, this ) );
+ count --;
+ }
+ }
+
+ AllPeersModified = 0;
+}
+
+void OTGateway::saveKnownPeers( void ) {
+ QDir SaveDir = QDir::home();
+
+ if( ! SaveDir.exists( "Settings" ) ) {
+ SaveDir.mkdir( "Settings" );
+ }
+ SaveDir.cd( "Settings" );
+
+ if( ! SaveDir.exists( "opietooth" ) ) {
+ SaveDir.mkdir( "opietooth" );
+ }
+ SaveDir.cd( "opietooth" );
+
+ QFile F( SaveDir.path() + "/SeenDevices.conf" );
+
+ if( F.open( IO_WriteOnly | IO_Truncate ) ) {
+ QTextStream TS(&F);
+ QString S;
+
+ TS << AllPeers.count() << endl;
+
+ for( unsigned int i = 0;
+ i < AllPeers.count();
+ i ++ ) {
+ AllPeers[i]->save( TS );
+ }
+ AllPeersModified = 0;
+ }
+ AllPeersModified = 0;
+}
+
+int OTGateway::connectedToRFCommChannel( const OTDeviceAddress & Addr,
+ int channel ) {
+
+ int s;
+
+ if( (s = ::socket(AF_BLUETOOTH, SOCK_RAW, BTPROTO_RFCOMM)) < 0 ) {
+ emit error( tr("Can't open RFCOMM control socket") );
+ return 0;
+ }
+
+ // get all rfcomm devices
+ { struct rfcomm_dev_list_req *dl;
+ struct rfcomm_dev_info *di, *dr;
+ int i;
+
+ dl = (struct rfcomm_dev_list_req *)alloca(
+ sizeof(*dl) + RFCOMM_MAX_DEV * sizeof(*di));
+ memset( dl, 0, sizeof(*dl) + RFCOMM_MAX_DEV * sizeof(*di) );
+ dl->dev_num = RFCOMM_MAX_DEV;
+ di = dl->dev_info;
+
+ if( ::ioctl(s, RFCOMMGETDEVLIST, (void *) dl) < 0) {
+ emit error( tr("Can't get device list") );
+ ::close( s );
+ return 0;
+ }
+
+ dr = di;
+ for (i = 0; i < dl->dev_num; i++, dr++) {
+ // connected to Peer
+ if( Addr == OTDeviceAddress( dr->dst ) &&
+ channel == dr->channel &&
+ ( dr->state != 0 )
+ ) {
+ // return device ID
+ return dr->id;
+ }
+ }
+ }
+
+ // no device
+ return -1;
+}
+
+static int byID( struct rfcomm_dev_info * d1,
+ struct rfcomm_dev_info * d2 ) {
+ return d1->id - d2->id;
+}
+
+int OTGateway::getFreeRFCommDevice( void ) {
+
+ int s;
+
+ if( (s = ::socket(AF_BLUETOOTH, SOCK_RAW, BTPROTO_RFCOMM)) < 0 ) {
+ emit error( tr("Can't open RFCOMM control socket") );
+ return 0;
+ }
+
+ // get all rfcomm devices
+ { struct rfcomm_dev_list_req *dl;
+ struct rfcomm_dev_info *di, *dr;
+ int i;
+
+ dl = (struct rfcomm_dev_list_req *)alloca(
+ sizeof(*dl) + RFCOMM_MAX_DEV * sizeof(*di));
+
+ dl->dev_num = RFCOMM_MAX_DEV;
+ di = dl->dev_info;
+
+ if( ::ioctl(s, RFCOMMGETDEVLIST, (void *) dl) < 0) {
+ emit error( tr("Can't get device list") );
+ ::close( s );
+ return 0;
+ }
+
+ // s
+ if( dl->dev_num ) {
+ qsort( di, sizeof(struct rfcomm_dev_info),
+ dl->dev_num, (int(*)(const void*,const void*))byID );
+ int id = 0;
+
+ dr = di;
+ // find lowest free device number
+ for (i = 0; i < dl->dev_num; i++, dr++) {
+ if( id != dr->id ) {
+ return id;
+ }
+ id ++;
+ }
+ return id;
+ } else {
+ return 0;
+ }
+ }
+}
+
+int OTGateway::releaseRFCommDevice( int devnr ) {
+
+ int s;
+
+ if( (s = ::socket(AF_BLUETOOTH, SOCK_RAW, BTPROTO_RFCOMM)) < 0 ) {
+ emit error( tr("Can't open RFCOMM control socket") );
+ return 0;
+ }
+
+ // get all rfcomm devices
+ { struct rfcomm_dev_list_req *dl;
+ struct rfcomm_dev_info *di, *dr;
+ int i;
+
+ dl = (struct rfcomm_dev_list_req *)alloca(
+ sizeof(*dl) + RFCOMM_MAX_DEV * sizeof(*di));
+ memset( dl, 0, sizeof(*dl) + RFCOMM_MAX_DEV * sizeof(*di) );
+ dl->dev_num = RFCOMM_MAX_DEV;
+ di = dl->dev_info;
+
+ if( ::ioctl(s, RFCOMMGETDEVLIST, (void *) dl) < 0) {
+ emit error( tr("Can't get device list") );
+ ::close( s );
+ return 0;
+ }
+
+ dr = di;
+ for (i = 0; i < dl->dev_num; i++, dr++) {
+ if( dr->id == devnr ) {
+ // still in connection list
+ struct rfcomm_dev_req req;
+ int err;
+
+ memset(&req, 0, sizeof(req));
+ req.dev_id = devnr;
+
+ if ((err = ioctl(s, RFCOMMRELEASEDEV, &req)) < 0 ) {
+ return err;
+ }
+ return 0;
+ }
+ }
+ }
+
+ // no device -> nothing to release eiterh
+ return 0;
+}
+