use dbus::arg::OwnedFd;
use futures::ready;
use libc::{AF_LOCAL, SOCK_CLOEXEC, SOCK_NONBLOCK, SOCK_SEQPACKET};
use pin_project::pin_project;
use std::{
mem::MaybeUninit,
os::unix::io::{AsRawFd, FromRawFd, IntoRawFd, RawFd},
pin::Pin,
task::{Context, Poll},
};
use strum::{Display, EnumString};
use tokio::{
io::{AsyncRead, AsyncWrite, ReadBuf},
net::UnixDatagram,
};
use crate::Address;
pub mod local;
pub mod remote;
pub(crate) const SERVICE_INTERFACE: &str = "org.bluez.GattService1";
pub(crate) const CHARACTERISTIC_INTERFACE: &str = "org.bluez.GattCharacteristic1";
pub(crate) const DESCRIPTOR_INTERFACE: &str = "org.bluez.GattDescriptor1";
define_flags!(pub CharacteristicFlags, "Bluetooth GATT characteristic flags." => {
broadcast ("broadcast"),
read ("read"),
write_without_response ("write-without-response"),
write ("write"),
notify ("notify"),
indicate ("indicate"),
authenticated_signed_writes ("authenticated-signed-writes"),
extended_properties ("extended-properties"),
reliable_write ("reliable-write"),
writable_auxiliaries ("writable-auxiliaries"),
encrypt_read ("encrypt-read"),
encrypt_write ("encrypt-write"),
encrypt_authenticated_read ("encrypt-authenticated-read"),
encrypt_authenticated_write ("encrypt-authenticated-write"),
secure_read ("secure-read"),
secure_write ("secure-write"),
authorize ("authorize"),
});
define_flags!(DescriptorFlags, "Bluetooth GATT characteristic descriptor flags." => {
read ("read"),
write ("write"),
encrypt_read ("encrypt-read"),
encrypt_write ("encrypt-write"),
encrypt_authenticated_read ("encrypt-authenticated-read"),
encrypt_authenticated_write ("encrypt-authenticated-write"),
secure_read ("secure-read"),
secure_write ("secure-write"),
authorize ("authorize"),
});
#[derive(Clone, Copy, Debug, Eq, PartialEq, Ord, PartialOrd, Hash, EnumString, Display)]
pub enum WriteOp {
#[strum(serialize = "command")]
Command,
#[strum(serialize = "request")]
Request,
#[strum(serialize = "reliable")]
Reliable,
}
impl Default for WriteOp {
fn default() -> Self {
Self::Command
}
}
#[pin_project]
#[derive(Debug)]
pub struct CharacteristicReader {
adapter_name: String,
device_address: Address,
mtu: usize,
#[pin]
socket: UnixDatagram,
buf: Vec<u8>,
}
impl CharacteristicReader {
pub fn adapter_name(&self) -> &str {
&self.adapter_name
}
pub fn device_address(&self) -> Address {
self.device_address
}
pub fn mtu(&self) -> usize {
self.mtu
}
pub async fn recvable(&self) -> std::io::Result<()> {
self.socket.readable().await
}
pub fn try_recv(&self) -> std::io::Result<Vec<u8>> {
let mut buf = Vec::with_capacity(self.mtu);
let n = self.socket.try_recv_buf(&mut buf)?;
buf.truncate(n);
Ok(buf)
}
pub async fn recv(&self) -> std::io::Result<Vec<u8>> {
loop {
self.recvable().await?;
match self.try_recv() {
Err(err) if err.kind() == std::io::ErrorKind::WouldBlock => continue,
res => return res,
}
}
}
pub fn into_raw_fd(self) -> std::io::Result<RawFd> {
Ok(self.socket.into_std()?.into_raw_fd())
}
}
impl AsyncRead for CharacteristicReader {
fn poll_read(mut self: Pin<&mut Self>, cx: &mut Context, buf: &mut ReadBuf) -> Poll<std::io::Result<()>> {
let buf_space = buf.remaining();
if !self.buf.is_empty() {
let to_read = buf_space.min(self.buf.len());
let remaining = self.buf.split_off(to_read);
buf.put_slice(&self.buf);
self.buf = remaining;
Poll::Ready(Ok(()))
} else if buf_space < self.mtu {
let this = self.project();
let mut mtu_buf: Vec<MaybeUninit<u8>> = vec![MaybeUninit::uninit(); *this.mtu];
let mut mtu_read_buf = ReadBuf::uninit(&mut mtu_buf);
ready!(this.socket.poll_recv(cx, &mut mtu_read_buf))?;
let n = mtu_read_buf.filled().len();
mtu_buf.truncate(n);
let mut mtu_buf: Vec<u8> = mtu_buf.into_iter().map(|v| unsafe { v.assume_init() }).collect();
*this.buf = mtu_buf.split_off(buf_space.min(n));
buf.put_slice(&mtu_buf);
Poll::Ready(Ok(()))
} else {
self.project().socket.poll_recv(cx, buf)
}
}
}
impl AsRawFd for CharacteristicReader {
fn as_raw_fd(&self) -> RawFd {
self.socket.as_raw_fd()
}
}
impl IntoRawFd for CharacteristicReader {
fn into_raw_fd(self) -> RawFd {
self.into_raw_fd().expect("into_raw_fd failed")
}
}
#[pin_project]
#[derive(Debug)]
pub struct CharacteristicWriter {
adapter_name: String,
device_address: Address,
mtu: usize,
#[pin]
socket: UnixDatagram,
}
impl CharacteristicWriter {
pub fn adapter_name(&self) -> &str {
&self.adapter_name
}
pub fn device_address(&self) -> Address {
self.device_address
}
pub fn mtu(&self) -> usize {
self.mtu
}
pub async fn closed(&self) -> std::io::Result<()> {
self.socket.readable().await
}
pub fn is_closed(&self) -> std::io::Result<bool> {
let mut buf = [0u8];
match self.socket.try_recv(&mut buf) {
Ok(_) => Ok(true),
Err(err) if err.kind() == std::io::ErrorKind::WouldBlock => Ok(false),
Err(err) => Err(err),
}
}
pub async fn sendable(&self) -> std::io::Result<()> {
self.socket.writable().await
}
pub fn try_send(&self, buf: &[u8]) -> std::io::Result<()> {
if buf.len() > self.mtu {
return Err(std::io::Error::new(std::io::ErrorKind::WriteZero, "data length exceeds MTU"));
}
match self.socket.try_send(buf) {
Ok(n) if n == buf.len() => Ok(()),
Ok(_) => Err(std::io::Error::new(std::io::ErrorKind::Other, "partial write occured")),
Err(err) => Err(err),
}
}
pub async fn send(&self, buf: &[u8]) -> std::io::Result<()> {
loop {
self.sendable().await?;
match self.try_send(buf) {
Err(err) if err.kind() == std::io::ErrorKind::WouldBlock => continue,
res => return res,
}
}
}
pub fn into_raw_fd(self) -> std::io::Result<RawFd> {
Ok(self.socket.into_std()?.into_raw_fd())
}
}
impl AsyncWrite for CharacteristicWriter {
fn poll_write(self: Pin<&mut Self>, cx: &mut std::task::Context, buf: &[u8]) -> Poll<std::io::Result<usize>> {
let max_len = buf.len().min(self.mtu);
let buf = &buf[..max_len];
self.project().socket.poll_send(cx, buf)
}
fn poll_flush(self: Pin<&mut Self>, _cx: &mut std::task::Context) -> Poll<std::io::Result<()>> {
Poll::Ready(Ok(()))
}
fn poll_shutdown(self: Pin<&mut Self>, _cx: &mut std::task::Context) -> Poll<std::io::Result<()>> {
Poll::Ready(Ok(()))
}
}
impl AsRawFd for CharacteristicWriter {
fn as_raw_fd(&self) -> RawFd {
self.socket.as_raw_fd()
}
}
impl IntoRawFd for CharacteristicWriter {
fn into_raw_fd(self) -> RawFd {
self.into_raw_fd().expect("into_raw_fd failed")
}
}
pub(crate) fn make_socket_pair(non_block: bool) -> std::io::Result<(OwnedFd, UnixDatagram)> {
let mut sv: [RawFd; 2] = [0; 2];
let mut ty = SOCK_SEQPACKET | SOCK_CLOEXEC;
if non_block {
ty |= SOCK_NONBLOCK;
}
if unsafe { libc::socketpair(AF_LOCAL, ty, 0, sv.as_mut_ptr()) } == -1 {
return Err(std::io::Error::last_os_error());
}
let [fd1, fd2] = sv;
let fd1 = unsafe { OwnedFd::new(fd1) };
let us = unsafe { std::os::unix::net::UnixDatagram::from_raw_fd(fd2) };
us.set_nonblocking(true)?;
let us = UnixDatagram::from_std(us)?;
Ok((fd1, us))
}
pub(crate) fn mtu_workaround(mtu: usize) -> usize {
mtu.saturating_sub(5)
}