bluetooth_serial_port/
bluetooth.rs

1use std::{
2    io::{Read, Write},
3    str, time,
4};
5
6use crate::platform;
7
8/// The bluetooth socket.
9///
10/// Can be used with `mio::Poll`.
11#[derive(Debug)]
12pub struct BtSocket(platform::BtSocket);
13
14impl BtSocket {
15    /// Create an (still) unconnected socket.
16    pub fn new(protocol: BtProtocol) -> Result<BtSocket, BtError> {
17        Ok(From::from(platform::BtSocket::new(protocol)?))
18    }
19
20    /// Connect to the RFCOMM service on remote device with address `addr`. Channel will be
21    /// determined through SDP protocol.
22    ///
23    /// This function can block for some seconds.
24    pub fn connect(&mut self, addr: BtAddr) -> Result<(), BtError> {
25        // Create temporary `mio` event loop
26        let evtloop = mio::Poll::new().unwrap();
27        let token = mio::Token(0);
28        let mut events = mio::Events::with_capacity(2);
29
30        // Request a socket connection
31        let mut connect = self.0.connect(addr);
32
33        loop {
34            match connect.advance()? {
35                BtAsync::WaitFor(evented, interest) => {
36                    let mut event_received = false;
37                    while !event_received {
38                        // Register this, single, event source
39                        evtloop
40                            .register(evented, token, interest, mio::PollOpt::oneshot())
41                            .unwrap();
42
43                        // Wait for it to transition to the requested state
44                        evtloop.poll(&mut events, None).unwrap();
45
46                        for event in events.iter() {
47                            if event.token() == token {
48                                event_received = true;
49                                evtloop.deregister(evented).unwrap();
50                            }
51                        }
52                    }
53                }
54
55                BtAsync::Done => {
56                    return Ok(());
57                }
58            }
59        }
60    }
61
62    /// Connect to the RFCOMM service on remote device with address `addr`. Channel will be
63    /// determined through SDP protocol.
64    ///
65    /// This function will return immediately and can therefor not indicate most kinds of failures.
66    /// Once the connection actually has been established or an error has been determined the socket
67    /// will become writable however. It is highly recommended to combine this call with the usage
68    /// of `mio` (or some higher level event loop) to get proper non-blocking behaviour.
69    pub fn connect_async(&mut self, addr: BtAddr) -> BtSocketConnect {
70        BtSocketConnect(self.0.connect(addr))
71    }
72}
73
74impl From<platform::BtSocket> for BtSocket {
75    fn from(socket: platform::BtSocket) -> BtSocket {
76        BtSocket(socket)
77    }
78}
79
80impl mio::Evented for BtSocket {
81    fn register(
82        &self,
83        poll: &mio::Poll,
84        token: mio::Token,
85        interest: mio::Ready,
86        opts: mio::PollOpt,
87    ) -> std::io::Result<()> {
88        self.0.register(poll, token, interest, opts)
89    }
90
91    fn reregister(
92        &self,
93        poll: &mio::Poll,
94        token: mio::Token,
95        interest: mio::Ready,
96        opts: mio::PollOpt,
97    ) -> std::io::Result<()> {
98        self.0.reregister(poll, token, interest, opts)
99    }
100
101    fn deregister(&self, poll: &mio::Poll) -> std::io::Result<()> {
102        self.0.deregister(poll)
103    }
104}
105
106impl Read for BtSocket {
107    fn read(&mut self, buf: &mut [u8]) -> std::io::Result<usize> {
108        self.0.read(buf)
109    }
110}
111
112impl Write for BtSocket {
113    fn write(&mut self, buf: &[u8]) -> std::io::Result<usize> {
114        self.0.write(buf)
115    }
116
117    fn flush(&mut self) -> std::io::Result<()> {
118        self.0.flush()
119    }
120}
121
122/// What needs to happen to advance to the next state an asynchronous process
123#[allow(missing_debug_implementations)] // `&mio::Evented` doesn't do `Debug`
124pub enum BtAsync<'a> {
125    /// Caller needs to wait for the given `Evented` object to reach the given `Ready` state
126    WaitFor(&'a mio::Evented, mio::Ready),
127
128    /// Asynchronous transaction has completed
129    Done,
130}
131
132/// Manages the bluetooth connection process when used from an asynchronous client.
133#[derive(Debug)]
134pub struct BtSocketConnect<'a>(platform::BtSocketConnect<'a>);
135
136impl<'a> BtSocketConnect<'a> {
137    /// Advance the connection process to the next state
138    ///
139    /// Usage: When receiving a new `BtSocketConnect` instance call this function to get the
140    /// connection process started, then wait for the condition requested in `BtAsync` to apply
141    /// (by polling for it in a `mio.Poll` instance in general). Once the condition is met, invoke
142    /// this function again to advance to the next connect step. Repeat this process until you reach
143    /// `BtAsync::Done`, then discard this object and enjoy your established connection.
144    pub fn advance(&mut self) -> Result<BtAsync, BtError> {
145        self.0.advance()
146    }
147}
148
149/// Finds a vector of Bluetooth devices in range.
150///
151/// This function blocks for some seconds.
152pub fn scan_devices(timeout: time::Duration) -> Result<Vec<BtDevice>, BtError> {
153    platform::scan_devices(timeout)
154}
155
156/// Represents an error which occurred in this library.
157#[derive(Debug)]
158pub enum BtError {
159    /// No specific information is known.
160    Unknown,
161
162    /// On Unix platforms: the error code and an explanation for this error code.
163    Errno(u32, String),
164
165    /// This error only has a description.
166    Desc(String),
167
168    /// `std::io::Error`
169    IoError(std::io::Error),
170}
171
172impl std::fmt::Display for BtError {
173    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
174        write!(f, "{:}", std::error::Error::description(self))
175    }
176}
177
178impl std::error::Error for BtError {
179    fn description(&self) -> &str {
180        match self {
181            BtError::Unknown => "Unknown Bluetooth Error",
182            BtError::Errno(_, ref message) => message.as_str(),
183            BtError::Desc(ref message) => message.as_str(),
184            BtError::IoError(ref err) => err.description(),
185        }
186    }
187}
188
189impl From<std::io::Error> for BtError {
190    fn from(error: std::io::Error) -> Self {
191        BtError::IoError(error)
192    }
193}
194
195/// A 6-byte long MAC address.
196#[repr(C, packed)]
197#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord)]
198pub struct BtAddr(pub [u8; 6]);
199
200impl std::fmt::Debug for BtAddr {
201    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
202        write!(
203            f,
204            "{:02X}:{:02X}:{:02X}:{:02X}:{:02X}:{:02X}",
205            self.0[0], self.0[1], self.0[2], self.0[3], self.0[4], self.0[5]
206        )
207    }
208}
209
210impl BtAddr {
211    /// Returns the MAC address `00:00:00:00:00:00`
212    pub fn any() -> BtAddr {
213        BtAddr([0, 0, 0, 0, 0, 0])
214    }
215
216    /// Linux lower-layers actually hold the address in native byte-order
217    /// althrough they are always displayed in network byte-order
218    #[doc(hidden)]
219    #[inline(always)]
220    #[cfg(target_endian = "little")]
221    pub fn convert_host_byteorder(mut self) -> BtAddr {
222        {
223            let (value_1, value_2) = (&mut self.0).split_at_mut(3);
224            std::mem::swap(&mut value_1[0], &mut value_2[2]);
225            std::mem::swap(&mut value_1[1], &mut value_2[1]);
226            std::mem::swap(&mut value_1[2], &mut value_2[0]);
227        }
228
229        self
230    }
231
232    #[doc(hidden)]
233    #[inline(always)]
234    #[cfg(target_endian = "big")]
235    pub fn convert_host_byteorder(self) -> BtAddr {
236        // Public address structure contents are always big-endian
237        self
238    }
239}
240
241impl ToString for BtAddr {
242    /// Converts `BtAddr` to a string of the format `XX:XX:XX:XX:XX:XX`.
243    fn to_string(&self) -> String {
244        format!(
245            "{:02X}:{:02X}:{:02X}:{:02X}:{:02X}:{:02X}",
246            self.0[0], self.0[1], self.0[2], self.0[3], self.0[4], self.0[5]
247        )
248    }
249}
250
251impl str::FromStr for BtAddr {
252    type Err = ();
253    /// Converts a string of the format `XX:XX:XX:XX:XX:XX` to a `BtAddr`.
254    fn from_str(s: &str) -> Result<Self, Self::Err> {
255        let splits_iter = s.split(':');
256        let mut addr = BtAddr::any();
257        let mut i = 0;
258        for split_str in splits_iter {
259            if i == 6 || split_str.len() != 2 {
260                return Err(());
261            } // only 6 values (0 <= i <= 5) are allowed
262            let high = (split_str.as_bytes()[0] as char).to_digit(16).ok_or(())?;
263            let low = (split_str.as_bytes()[1] as char).to_digit(16).ok_or(())?;
264            addr.0[i] = (high * 16 + low) as u8;
265            i += 1;
266        }
267        if i != 6 {
268            return Err(());
269        }
270        Ok(addr)
271    }
272}
273
274/// A device with its a name and address.
275#[derive(Debug, Clone, PartialEq, Eq)]
276pub struct BtDevice {
277    /// The name of the device.
278    pub name: String,
279
280    /// The MAC address of the device.
281    pub addr: BtAddr,
282}
283
284/// The Bluetooth protocol you can use with this libary.
285///
286/// Will probably be always `RFCOMM`.
287#[derive(Clone, Copy, Debug)]
288pub enum BtProtocol {
289    // L2CAP = BTPROTO_L2CAP,
290    // HCI = BTPROTO_HCI,
291    // SCO = BTPROTO_SCO,
292    // BNEP = BTPROTO_BNEP,
293    // CMTP = BTPROTO_CMTP,
294    // HIDP = BTPROTO_HIDP,
295    // AVDTP = BTPROTO_AVDTP
296    /// Serial RFCOMM connection to a bluetooth device.
297    RFCOMM, // = BTPROTO_RFCOMM */
298}
299
300impl BtDevice {
301    /// Create a new `BtDevice` manually from a name and addr.
302    pub fn new(name: String, addr: BtAddr) -> BtDevice {
303        BtDevice { name, addr }
304    }
305}
306
307#[cfg(test)]
308mod tests {
309    use super::*;
310    use std::str::FromStr;
311
312    #[test()]
313    fn btaddr_from_string() {
314        match BtAddr::from_str("00:00:00:00:00:00") {
315            Ok(addr) => assert_eq!(addr, BtAddr([0u8; 6])),
316            Err(_) => panic!(""),
317        }
318
319        let fail_strings = [
320            "addr : String",
321            "00:00:00:00:00",
322            "00:00:00:00:00:00:00",
323            "-00:00:00:00:00:00",
324            "0G:00:00:00:00:00",
325        ];
326        for &s in &fail_strings {
327            match BtAddr::from_str(s) {
328                Ok(_) => panic!("Somehow managed to parse \"{}\" as an address?!", s),
329                Err(_) => (),
330            }
331        }
332    }
333
334    #[test()]
335    fn btaddr_to_string() {
336        assert_eq!(BtAddr::any().to_string(), "00:00:00:00:00:00");
337        assert_eq!(BtAddr([1, 2, 3, 4, 5, 6]).to_string(), "01:02:03:04:05:06");
338    }
339
340    #[test()]
341    fn btaddr_roundtrips_to_from_str() {
342        let addr = BtAddr([0, 22, 4, 1, 33, 192]);
343        let addr_string = "00:ff:ee:ee:dd:12";
344
345        assert_eq!(addr, BtAddr::from_str(&addr.to_string()).unwrap());
346        assert!(
347            addr_string.eq_ignore_ascii_case(&BtAddr::from_str(addr_string).unwrap().to_string())
348        );
349    }
350
351    #[cfg(not(feature = "test_without_hardware"))]
352    #[test()]
353    fn creates_rfcomm_socket() {
354        BtSocket::new(BtProtocol::RFCOMM).unwrap();
355    }
356
357    #[cfg(not(feature = "test_without_hardware"))]
358    #[test()]
359    fn scans_devices() {
360        scan_devices(time::Duration::from_secs(20)).unwrap();
361    }
362}