/*
* Copyright 2013 Canonical Ltd.
*
* 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; version 3.
*
* 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 Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program. If not, see .
*
* Authors:
* Charles Kerr
*/
/**
* Monitors whether or not bluetooth is blocked,
* either by software (e.g., a session configuration setting)
* or by hardware (e.g., user disabled it via a physical switch on her laptop)
*/
public class KillSwitch: Object
{
public bool blocked { get; protected set; default = false; }
public void try_set_blocked (bool blocked)
{
return_if_fail (this.blocked != blocked);
// write a 'soft kill' event to fkill
var event = Linux.RfKillEvent() {
op = Linux.RfKillOp.CHANGE_ALL,
type = Linux.RfKillType.BLUETOOTH,
soft = (uint8)blocked
};
var bwritten = Posix.write (fd, &event, sizeof(Linux.RfKillEvent));
if (bwritten == -1)
warning ("Could not write rfkill event: %s", strerror(errno));
}
/***
**** Past this point, it's all RfKill implementation details...
***/
private class Entry
{
public uint32 idx;
public Linux.RfKillType type;
public bool soft;
public bool hard;
}
private HashTable entries;
private int fd;
private IOChannel channel;
private uint watch;
private bool calculate_blocked ()
{
foreach (Entry entry in entries.get_values())
if (entry.soft || entry.hard)
return true;
return false;
}
~KillSwitch ()
{
Source.remove (watch);
Posix.close (fd);
}
public KillSwitch ()
{
entries = new HashTable(direct_hash, direct_equal);
var path = "/dev/rfkill";
fd = Posix.open (path, Posix.O_RDWR | Posix.O_NONBLOCK );
message ("fd is %d", fd);
if (fd == -1)
{
warning (@"Can't open $path: $(strerror(errno)); KillSwitch disable");
}
else
{
// read everything that's already there, then watch for more
while (read_event());
channel = new IOChannel.unix_new (fd);
watch = channel.add_watch (IOCondition.IN, on_channel_event);
}
}
private bool on_channel_event (IOChannel source, IOCondition condition)
{
read_event ();
return true;
}
private bool read_event ()
{
assert (fd != -1);
var event = Linux.RfKillEvent();
var n = sizeof (Linux.RfKillEvent);
var bytesread = Posix.read (fd, &event, n);
if (bytesread == n)
{
process_event (event);
return true;
}
return false;
}
private void process_event (Linux.RfKillEvent event)
{
// we only want things that affect bluetooth
if ((event.type != Linux.RfKillType.ALL) &&
(event.type != Linux.RfKillType.BLUETOOTH))
return;
switch (event.op)
{
case Linux.RfKillOp.CHANGE:
case Linux.RfKillOp.ADD:
Entry entry = new Entry ();
entry.idx = event.idx;
entry.type = event.type;
entry.soft = event.soft != 0;
entry.hard = event.hard != 0;
entries.insert (entry.idx, entry);
break;
case Linux.RfKillOp.DEL:
entries.remove (event.idx);
break;
}
// update the 'blocked' property
blocked = calculate_blocked ();
}
}