1 // Copyright 2019 The Chromium OS Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4 use std::os::unix::io::{AsRawFd, RawFd};
5 use std::{io, mem};
6 
7 use cras_sys::gen::{cras_disconnect_stream_message, cras_server_message, CRAS_SERVER_MESSAGE_ID};
8 use sys_util::{net::UnixSeqpacket, ScmSocket};
9 
10 use data_model::DataInit;
11 
12 /// Server socket type to connect.
13 pub enum CrasSocketType {
14     /// A server socket type supports only playback function.
15     Legacy,
16     /// A server socket type supports both playback and capture functions.
17     Unified,
18 }
19 
20 impl CrasSocketType {
sock_path(&self) -> &str21     fn sock_path(&self) -> &str {
22         match self {
23             Self::Legacy => "/run/cras/.cras_socket",
24             Self::Unified => "/run/cras/.cras_unified",
25         }
26     }
27 }
28 
29 /// A socket connecting to the CRAS audio server.
30 pub struct CrasServerSocket {
31     socket: UnixSeqpacket,
32 }
33 
34 impl CrasServerSocket {
new() -> io::Result<CrasServerSocket>35     pub fn new() -> io::Result<CrasServerSocket> {
36         Self::with_type(CrasSocketType::Legacy)
37     }
38 
39     /// Creates a `CrasServerSocket` with given `CrasSocketType`.
40     ///
41     /// # Errors
42     ///
43     /// Returns the `io::Error` generated when connecting to the socket on failure.
with_type(socket_type: CrasSocketType) -> io::Result<CrasServerSocket>44     pub fn with_type(socket_type: CrasSocketType) -> io::Result<CrasServerSocket> {
45         Ok(CrasServerSocket {
46             socket: UnixSeqpacket::connect(socket_type.sock_path())?,
47         })
48     }
49 
50     /// Sends a sized and packed server messge to the server socket. The message
51     /// must implement `Sized` and `DataInit`.
52     /// # Arguments
53     /// * `message` - A sized and packed message.
54     /// * `fds` - A slice of fds to send.
55     ///
56     /// # Returns
57     /// * Length of written bytes in `usize`.
58     ///
59     /// # Errors
60     /// Return error if the socket fails to write message to server.
send_server_message_with_fds<M: Sized + DataInit>( &self, message: &M, fds: &[RawFd], ) -> io::Result<usize>61     pub fn send_server_message_with_fds<M: Sized + DataInit>(
62         &self,
63         message: &M,
64         fds: &[RawFd],
65     ) -> io::Result<usize> {
66         match fds.len() {
67             0 => self.socket.send(message.as_slice()),
68             _ => {
69                 let ioslice = io::IoSlice::new(message.as_slice());
70                 match self.send_with_fds(&[ioslice], fds) {
71                     Ok(len) => Ok(len),
72                     Err(err) => Err(io::Error::new(io::ErrorKind::Other, format!("{}", err))),
73                 }
74             }
75         }
76     }
77 
78     /// Creates a clone of the underlying socket. The returned clone can also be
79     /// used to communicate with the cras server.
try_clone(&self) -> io::Result<CrasServerSocket>80     pub fn try_clone(&self) -> io::Result<CrasServerSocket> {
81         let new_sock = self.socket.try_clone()?;
82         Ok(CrasServerSocket { socket: new_sock })
83     }
84 
85     /// Send a message to request disconnection of the given stream.
86     ///
87     /// Builds a `cras_disconnect_stream_message` containing `stream_id` and
88     /// sends it to the server.
89     /// No response is expected.
90     ///
91     /// # Arguments
92     ///
93     /// * `stream_id` - The id of the stream that should be disconnected.
94     ///
95     /// # Errors
96     ///
97     /// * If the message was not written to the server socket successfully.
disconnect_stream(&self, stream_id: u32) -> io::Result<()>98     pub fn disconnect_stream(&self, stream_id: u32) -> io::Result<()> {
99         let msg_header = cras_server_message {
100             length: mem::size_of::<cras_disconnect_stream_message>() as u32,
101             id: CRAS_SERVER_MESSAGE_ID::CRAS_SERVER_DISCONNECT_STREAM,
102         };
103         let server_cmsg = cras_disconnect_stream_message {
104             header: msg_header,
105             stream_id,
106         };
107         self.send_server_message_with_fds(&server_cmsg, &[])
108             .map(|_| ())
109     }
110 }
111 
112 // For using `recv_with_fds` and `send_with_fds`.
113 impl ScmSocket for CrasServerSocket {
socket_fd(&self) -> RawFd114     fn socket_fd(&self) -> RawFd {
115         self.socket.as_raw_fd()
116     }
117 }
118 
119 // For using `PollContex`.
120 impl AsRawFd for CrasServerSocket {
as_raw_fd(&self) -> RawFd121     fn as_raw_fd(&self) -> RawFd {
122         self.socket.as_raw_fd()
123     }
124 }
125