blob: 8e30a75a87073a34d71bdc2b97c6464580fcee45 [file] [log] [blame]
// Copyright 2023 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
use super::h4::PacketError;
use crate::devices::chip::{self, ChipIdentifier};
use crate::devices::devices_handler::{add_chip, remove_chip};
use crate::echip;
use crate::echip::packet::{register_transport, unregister_transport, Response};
use crate::transport::h4;
use log::{error, info, warn};
use netsim_proto::common::ChipKind;
use std::io::{ErrorKind, Write};
use std::net::{Ipv4Addr, SocketAddrV4, TcpListener, TcpStream};
use std::thread;
// The HCI server implements the Bluetooth UART transport protocol
// (a.k.a. H4) over TCP. Each new connection on the HCI port spawns a
// new virtual controller associated with a new device.
/// Start the socket-based transport.
///
/// The socket transport reads/writes host-controller messages
/// for bluetooth (h4 hci) over a [TcpStream] transport.
///
struct SocketTransport {
stream: TcpStream,
}
impl Response for SocketTransport {
fn response(&mut self, packet: Vec<u8>, packet_type: u8) {
let mut buffer = Vec::new();
buffer.push(packet_type);
buffer.extend(packet);
if let Err(e) = self.stream.write_all(&buffer[..]) {
error!("error writing {}", e);
};
}
}
pub fn run_socket_transport(hci_port: u16) {
thread::Builder::new()
.name("hci_transport".to_string())
.spawn(move || {
accept_incoming(hci_port)
.unwrap_or_else(|e| error!("Failed to accept incoming stream: {:?}", e));
})
.unwrap();
}
fn accept_incoming(hci_port: u16) -> std::io::Result<()> {
let hci_socket = SocketAddrV4::new(Ipv4Addr::UNSPECIFIED, hci_port);
let listener = TcpListener::bind(hci_socket)?;
info!("Hci socket server is listening on: {}", hci_port);
for stream in listener.incoming() {
let stream = stream?;
// the socket address of the remote peer of this TCP connection
info!("Hci client address: {}", stream.peer_addr().unwrap());
thread::Builder::new()
.name("hci_transport client".to_string())
.spawn(move || {
handle_hci_client(stream);
})
.unwrap();
}
Ok(())
}
fn handle_hci_client(stream: TcpStream) {
// ...
let chip_create_params = chip::CreateParams {
kind: ChipKind::BLUETOOTH,
address: String::new(),
name: Some(format!("socket-{}", stream.peer_addr().unwrap())),
manufacturer: "Google".to_string(),
product_name: "Google".to_string(),
bt_properties: None,
};
#[cfg(not(test))]
let echip_create_params = echip::CreateParam::Bluetooth(echip::bluetooth::CreateParams {
address: String::new(),
bt_properties: None,
});
#[cfg(test)]
let echip_create_params =
echip::CreateParam::Mock(echip::mocked::CreateParams { chip_kind: ChipKind::BLUETOOTH });
let result = match add_chip(
&stream.peer_addr().unwrap().port().to_string(),
&format!("socket-{}", stream.peer_addr().unwrap()),
&chip_create_params,
&echip_create_params,
) {
Ok(chip_result) => chip_result,
Err(err) => {
warn!("{err}");
return;
}
};
let tcp_rx = stream.try_clone().unwrap();
register_transport(result.chip_id, Box::new(SocketTransport { stream }));
let _ = reader(tcp_rx, ChipKind::BLUETOOTH, result.chip_id);
// unregister before remove_chip because facade may re-use facade_id
// on an intertwining create_chip and the unregister here might remove
// the recently added chip creating a disconnected transport.
unregister_transport(result.chip_id);
if let Err(err) = remove_chip(result.device_id, result.chip_id) {
warn!("{err}");
};
info!("Removed chip: device_id: {} chip_id: {}.", result.device_id, result.chip_id);
}
/// read from the socket and pass to the packet hub.
///
fn reader(mut tcp_rx: TcpStream, kind: ChipKind, chip_id: ChipIdentifier) -> std::io::Result<()> {
loop {
if let ChipKind::BLUETOOTH = kind {
match h4::read_h4_packet(&mut tcp_rx) {
Ok(mut packet) => {
echip::handle_request(chip_id, &mut packet.payload, packet.h4_type);
}
Err(PacketError::IoError(e)) if e.kind() == ErrorKind::UnexpectedEof => {
info!("End socket reader connection with {}.", &tcp_rx.peer_addr().unwrap());
return Ok(());
}
Err(e) => {
error!("End socket reader connection with {}. Failed to reading hci control packet: {:?}", &tcp_rx.peer_addr().unwrap(), e);
return Ok(());
}
}
}
}
}