1 //! This library provides UHID for HFP to interact with WebHID.
2 
3 use bt_topshim::topstack;
4 use log::debug;
5 use std::convert::TryFrom;
6 use std::fs::{File, OpenOptions};
7 use std::io::{self, Read, Write};
8 use std::os::unix::fs::OpenOptionsExt;
9 use std::path::Path;
10 pub use uhid_virt::OutputEvent;
11 use uhid_virt::{Bus, CreateParams, InputEvent, StreamError, UHID_EVENT_SIZE};
12 
13 pub const BLUETOOTH_TELEPHONY_UHID_REPORT_ID: u8 = 1;
14 pub const UHID_INPUT_NONE: u8 = 0;
15 pub const UHID_INPUT_HOOK_SWITCH: u8 = 1 << 0;
16 pub const UHID_INPUT_PHONE_MUTE: u8 = 1 << 1;
17 pub const UHID_INPUT_DROP: u8 = 1 << 2;
18 pub const UHID_OUTPUT_NONE: u8 = 0;
19 pub const UHID_OUTPUT_RING: u8 = 1 << 0;
20 pub const UHID_OUTPUT_OFF_HOOK: u8 = 1 << 1;
21 pub const UHID_OUTPUT_MUTE: u8 = 1 << 2;
22 
23 const RDESC: [u8; 57] = [
24     0x05,
25     0x0B, // Usage Page (Telephony)
26     0x09,
27     0x05, // Usage (Headset)
28     0xA1,
29     0x01, // Collection (Application)
30     0x85,
31     BLUETOOTH_TELEPHONY_UHID_REPORT_ID, //   Report ID (1)
32     0x05,
33     0x0B, //   Usage Page (Telephony)
34     0x15,
35     0x00, //   Logical Minimum (0)
36     0x25,
37     0x01, //   Logical Maximum (1)
38     0x09,
39     0x20, //   Usage (Hook Switch)
40     0x09,
41     0x2f, //   Usage (Phone Mute)
42     0x09,
43     0x26, //   Usage (Drop)
44     0x75,
45     0x01, //   Report Size (1)
46     0x95,
47     0x03, //   Report Count (3)
48     0x81,
49     //   Input (Data,Var,Abs,No Wrap,Linear,No Preferred State,No Null Position,
50     //          Non-volatile)
51     0x22,
52     0x75,
53     0x01, //   Report Size (1)
54     0x95,
55     0x05, //   Report Count (5)
56     0x81,
57     0x01, //   Input
58     0x05,
59     0x08, //   Usage Page (LEDs)
60     0x15,
61     0x00, //   Logical Minimum (0)
62     0x25,
63     0x01, //   Logical Maximum (1)
64     0x09,
65     0x18, //   Usage (Ring)
66     0x09,
67     0x17, //   Usage (Off-Hook)
68     0x09,
69     0x09, //   Usage (Mute)
70     0x75,
71     0x01, //   Report Size (1)
72     0x95,
73     0x03, //   Report Count (3)
74     0x91,
75     //   Output (Data,Var,Abs,No Wrap,Linear,No Preferred State,
76     //           No Null Position, Non-volatile)
77     0x22,
78     0x75,
79     0x01, //   Report Size (1)
80     0x95,
81     0x05, //   Report Count (5)
82     0x91,
83     0x01, //   Output
84     0xC0, // End Collection
85 ];
86 
87 pub struct UHidHfp {
88     handle: File,
89 }
90 
91 impl UHidHfp {
create<F>( adapter_addr: String, remote_addr: String, remote_name: String, output_callback: F, ) -> UHidHfp where F: Fn(OutputEvent) + std::marker::Send + 'static,92     pub fn create<F>(
93         adapter_addr: String,
94         remote_addr: String,
95         remote_name: String,
96         output_callback: F,
97     ) -> UHidHfp
98     where
99         F: Fn(OutputEvent) + std::marker::Send + 'static,
100     {
101         let rd_data = RDESC.to_vec();
102         let create_params = CreateParams {
103             name: remote_name,
104             phys: adapter_addr,
105             uniq: remote_addr.clone(),
106             bus: Bus::BLUETOOTH,
107             vendor: 0,
108             product: 0,
109             version: 0,
110             country: 0,
111             rd_data,
112         };
113 
114         let create_event: [u8; UHID_EVENT_SIZE] = InputEvent::Create(create_params).into();
115         let mut options = OpenOptions::new();
116         options.read(true);
117         options.write(true);
118         if cfg!(unix) {
119             options.custom_flags(libc::O_RDWR | libc::O_CLOEXEC);
120         }
121         let mut uhid_writer = options.open(Path::new("/dev/uhid")).unwrap();
122         let mut uhid_reader = uhid_writer.try_clone().unwrap();
123         uhid_writer.write_all(&create_event).unwrap();
124 
125         topstack::get_runtime().spawn_blocking(move || {
126             let mut event = [0u8; UHID_EVENT_SIZE];
127             debug!("UHID: reading loop start");
128             loop {
129                 match uhid_reader.read_exact(&mut event) {
130                     Err(e) => {
131                         log::error!("UHID: Read error: {:?}", e);
132                         break;
133                     }
134                     Ok(_) => (),
135                 }
136                 match OutputEvent::try_from(event) {
137                     Ok(m) => {
138                         match m {
139                             OutputEvent::Stop => break,
140                             _ => (),
141                         };
142 
143                         output_callback(m);
144                     }
145                     Err(e) => {
146                         match e {
147                             StreamError::Io(e) => log::error!("UHID: IO error: {}", e),
148                             StreamError::UnknownEventType(e) => {
149                                 log::error!("UHID: Unknown event type: {}", e)
150                             }
151                         }
152                         break;
153                     }
154                 }
155             }
156             debug!("UHID: reading loop completed");
157         });
158 
159         UHidHfp { handle: uhid_writer }
160     }
161 
destroy(&mut self) -> io::Result<()>162     pub fn destroy(&mut self) -> io::Result<()> {
163         let destroy_event: [u8; UHID_EVENT_SIZE] = InputEvent::Destroy.into();
164         self.handle.write_all(&destroy_event)
165     }
166 
send_input(&mut self, report: u8) -> io::Result<()>167     pub fn send_input(&mut self, report: u8) -> io::Result<()> {
168         let data: [u8; 2] = [BLUETOOTH_TELEPHONY_UHID_REPORT_ID, report];
169         let input_event: [u8; UHID_EVENT_SIZE] = InputEvent::Input { data: &data }.into();
170         self.handle.write_all(&input_event)
171     }
172 }
173