1#![macro_use]
2
3use libc::{c_char, c_int, c_void, free};
4use core::error::Error as StdError;
5use core::ffi::CStr;
6use core::{fmt, str};
7use ::alloc::string::{String, ToString};
8
9#[derive(Clone, PartialEq, Copy)]
16pub struct Error(&'static str, c_int);
17
18pub type Result<T> = ::core::result::Result<T, Error>;
19
20#[cfg(not(feature = "std"))]
21extern "C" {
22 pub(crate) static errno: c_int;
23}
24
25macro_rules! acheck {
26 ($f: ident ( $($x: expr),* ) ) => {{
27 let r = unsafe { alsa::$f( $($x),* ) };
28 if r < 0 { Err(Error::new(stringify!($f), -r as ::libc::c_int)) }
29 else { Ok(r) }
30 }}
31}
32
33pub fn from_const<'a>(func: &'static str, s: *const c_char) -> Result<&'a str> {
34 if s.is_null() {
35 return Err(invalid_str(func));
36 };
37 let cc = unsafe { CStr::from_ptr(s) };
38 str::from_utf8(cc.to_bytes()).map_err(|_| invalid_str(func))
39}
40
41pub fn from_alloc(func: &'static str, s: *mut c_char) -> Result<String> {
42 if s.is_null() {
43 return Err(invalid_str(func));
44 };
45 let c = unsafe { CStr::from_ptr(s) };
46 let ss = str::from_utf8(c.to_bytes())
47 .map_err(|_| {
48 unsafe {
49 free(s as *mut c_void);
50 }
51 invalid_str(func)
52 })?
53 .to_string();
54 unsafe {
55 free(s as *mut c_void);
56 }
57 Ok(ss)
58}
59
60pub fn from_code(func: &'static str, r: c_int) -> Result<c_int> {
61 if r < 0 {
62 Err(Error::new(func, r))
63 } else {
64 Ok(r)
65 }
66}
67
68impl Error {
69 pub fn new(func: &'static str, res: c_int) -> Error {
70 Self(func, res)
71 }
72
73 pub fn last(func: &'static str) -> Error {
74 #[cfg(feature = "std")] {
75 Self(
76 func,
77 std::io::Error::last_os_error().raw_os_error().unwrap_or_default(),
78 )
79 }
80 #[cfg(not(feature = "std"))] {
81 Self(
82 func,
83 unsafe {errno},
84 )
85 }
86 }
87
88 pub fn unsupported(func: &'static str) -> Error {
89 Error(func, libc::ENOTSUP)
90 }
91
92 pub fn func(&self) -> &'static str {
94 self.0
95 }
96
97 pub fn errno(&self) -> i32 {
102 self.1
103 }
104}
105
106pub fn invalid_str(func: &'static str) -> Error {
107 Error(func, libc::EILSEQ)
108}
109
110impl StdError for Error {
111 fn description(&self) -> &str {
112 "ALSA error"
113 }
114}
115
116impl fmt::Debug for Error {
117 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
118 write!(f, "{}", self)
119 }
120}
121
122impl fmt::Display for Error {
123 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
124 write!(
125 f,
126 "ALSA function '{}' failed with error '{} ({})'",
127 self.0,
128 desc(self.1),
129 self.1,
130 )
131 }
132}
133
134fn desc(err: i32) -> &'static str {
139 match err {
140 libc::EPERM => "Operation not permitted",
141 libc::ENOENT => "No such file or directory",
142 libc::ESRCH => "No such process",
143 libc::EINTR => "Interrupted system call",
144 libc::EIO => "I/O error",
145 libc::ENXIO => "No such device or address",
146 libc::E2BIG => "Argument list too long",
147 libc::ENOEXEC => "Exec format error",
148 libc::EBADF => "Bad file number",
149 libc::ECHILD => "No child processes",
150 libc::EAGAIN => "Try again",
151 libc::ENOMEM => "Out of memory",
152 libc::EACCES => "Permission denied",
153 libc::EFAULT => "Bad address",
154 libc::ENOTBLK => "Block device required",
155 libc::EBUSY => "Device or resource busy",
156 libc::EEXIST => "File exists",
157 libc::EXDEV => "Cross-device link",
158 libc::ENODEV => "No such device",
159 libc::ENOTDIR => "Not a directory",
160 libc::EISDIR => "Is a directory",
161 libc::EINVAL => "Invalid argument",
162 libc::ENFILE => "File table overflow",
163 libc::EMFILE => "Too many open files",
164 libc::ENOTTY => "Not a typewriter",
165 libc::ETXTBSY => "Text file busy",
166 libc::EFBIG => "File too large",
167 libc::ENOSPC => "No space left on device",
168 libc::ESPIPE => "Illegal seek",
169 libc::EROFS => "Read-only file system",
170 libc::EMLINK => "Too many links",
171 libc::EPIPE => "Broken pipe",
172 libc::EDOM => "Math argument out of domain of func",
173 libc::ERANGE => "Math result not representable",
174 libc::EDEADLK => "Resource deadlock would occur",
175 libc::ENAMETOOLONG => "File name too long",
176 libc::ENOLCK => "No record locks available",
177 libc::ENOSYS => "Function not implemented",
178 libc::ENOTEMPTY => "Directory not empty",
179 libc::ELOOP => "Too many symbolic links encountered",
180 libc::ENOMSG => "No message of desired type",
181 libc::EIDRM => "Identifier removed",
182 libc::EINPROGRESS => "Operation now in progress",
183 libc::EALREADY => "Operation already in progress",
184 libc::ENOTSOCK => "Socket operation on non-socket",
185 libc::EDESTADDRREQ => "Destination address required",
186 libc::EMSGSIZE => "Message too long",
187 libc::EPROTOTYPE => "Protocol wrong type for socket",
188 libc::ENOPROTOOPT => "Protocol not available",
189 libc::EPROTONOSUPPORT => "Protocol not supported",
190 libc::ESOCKTNOSUPPORT => "Socket type not supported",
191 libc::EPFNOSUPPORT => "Protocol family not supported",
192 libc::EAFNOSUPPORT => "Address family not supported by protocol",
193 libc::EADDRINUSE => "Address already in use",
194 libc::EADDRNOTAVAIL => "Cannot assign requested address",
195 libc::ENETDOWN => "Network is down",
196 libc::ENETUNREACH => "Network is unreachable",
197 libc::ENETRESET => "Network dropped connection because of reset",
198 libc::ECONNABORTED => "Software caused connection abort",
199 libc::ECONNRESET => "Connection reset by peer",
200 libc::ENOBUFS => "No buffer space available",
201 libc::EISCONN => "Transport endpoint is already connected",
202 libc::ENOTCONN => "Transport endpoint is not connected",
203 libc::ESHUTDOWN => "Cannot send after transport endpoint shutdown",
204 libc::ETOOMANYREFS => "Too many references: cannot splice",
205 libc::ETIMEDOUT => "Connection timed out",
206 libc::ECONNREFUSED => "Connection refused",
207 libc::EHOSTDOWN => "Host is down",
208 libc::EHOSTUNREACH => "No route to host",
209 libc::ENOTSUP => "Operation not supported",
210 _ => "Unknown errno",
211 }
212}
213
214impl From<Error> for fmt::Error {
215 fn from(_: Error) -> fmt::Error {
216 fmt::Error
217 }
218}
219
220#[test]
221fn broken_pcm_name() {
222 use ::alloc::ffi::CString;
223 let e = crate::PCM::open(
224 &*CString::new("this_PCM_does_not_exist").unwrap(),
225 crate::Direction::Playback,
226 false,
227 )
228 .err()
229 .unwrap();
230 assert_eq!(e.func(), "snd_pcm_open");
231 assert_eq!(e.errno(), libc::ENOENT);
232}