1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178
|
/*
* 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
* Authors:
* Charles Kerr <charles.kerr@canonical.com>
*/
/**
* 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).
*
* KillSwitchBluetooth uses this as the impl for its Bluetooth.blocked property
*/
public interface KillSwitch: Object
{
public abstract bool is_valid ();
public abstract bool blocked { get; protected set; }
/* Try to block/unblock bluetooth.
* This can fail if the requested state is overruled by a hardware block. */
public abstract void try_set_blocked (bool blocked);
}
/**
* KillSwitch impementation for Linux using /dev/rfkill
*/
public class RfKillSwitch: KillSwitch, Object
{
public bool blocked { get; protected set; default = false; }
public void try_set_blocked (bool blocked)
{
return_if_fail (this.blocked != blocked);
// Try to soft-block all the bluetooth devices
var event = Linux.RfKillEvent() {
op = Linux.RfKillOp.CHANGE_ALL,
type = Linux.RfKillType.BLUETOOTH,
soft = (uint8)blocked
};
/* Write this request to rfkill.
* Don't update this object's "blocked" property here --
* We'll get the update when on_channel_event() reads it below */
var bwritten = Posix.write (fd, &event, sizeof(Linux.RfKillEvent));
if (bwritten == -1)
warning (@"Could not write rfkill event: $(strerror(errno))");
}
/* represents an entry that we've read from the rfkill file */
private class Entry
{
public uint32 idx;
public Linux.RfKillType type;
public bool soft;
public bool hard;
}
private HashTable<uint32,Entry> entries;
private int fd = -1;
private IOChannel channel;
private uint watch;
protected override void dispose ()
{
if (watch != 0)
{
Source.remove (watch);
watch = 0;
}
if (fd != -1)
{
Posix.close (fd);
fd = -1;
}
base.dispose ();
}
public RfKillSwitch ()
{
entries = new HashTable<uint32,Entry>(direct_hash, direct_equal);
var path = "/dev/rfkill";
fd = Posix.open (path, Posix.O_RDWR | Posix.O_NONBLOCK );
if (fd == -1)
{
warning (@"Can't open $path for use as a killswitch backend: $(strerror(errno))");
}
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);
}
}
public bool is_valid ()
{
return fd != -1;
}
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;
default:
break;
}
/* update our blocked property.
it should be true if any bluetooth entry is hard- or soft-blocked */
var b = false;
foreach (var entry in entries.get_values ())
if ((b = (entry.soft || entry.hard)))
break;
blocked = b;
}
}
|