diff --git a/Cargo.toml b/Cargo.toml index 2790b55a..eeb6a727 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -22,7 +22,7 @@ nix = { version = "0.26", default-features = false, features = ["fs", "ioctl", " libudev = { version = "0.3.0", optional = true } unescaper = "0.1.3" -[target.'cfg(any(target_os = "ios", target_os = "macos"))'.dependencies] +[target.'cfg(target_vendor = "apple")'.dependencies] # TODO: Remove pinning this dependency when we are bumping our MSRV. core-foundation = "=0.10.0" core-foundation-sys = "0.8.4" diff --git a/README.md b/README.md index 6a782dcf..55ed69a2 100644 --- a/README.md +++ b/README.md @@ -152,7 +152,7 @@ demand. - `i686-unknown-linux-musl` - `x86_64-unknown-linux-gnu` - `x86_64-unknown-linux-musl` -- macOS/iOS +- macOS/iOS/tvOS/watchOS/visionOS - `aarch64-apple-darwin` - `aarch64-apple-ios` - `x86_64-apple-darwin` diff --git a/doc/platforms.md b/doc/platforms.md index 9c0fd329..247be35a 100644 --- a/doc/platforms.md +++ b/doc/platforms.md @@ -33,8 +33,8 @@ The BSDs basically **only** have the Termios2 API, but they call it Termios. It * https://man.openbsd.org/tty.4 -## macOS and iOS +## Darwin -While macOS and iOS have the heritage of a BSD, their support is slightly different. In theory, they support arbitrary baud rates in their Termios API much like the BSDs, but in practice this doesn't work with many hardware devices, as it's dependent on driver support. Instead, Apple added the `IOSSIOSPEED` ioctl in Mac OS X 10.4, which can set the baud rate to an arbitrary value. As the oldest macOS version supported by Rust is 10.7, it's available on all Mac platforms. +While macOS, iOS, and similar Apple platforms have the heritage of a BSD, their support is slightly different. In theory, they support arbitrary baud rates in their Termios API much like the BSDs, but in practice this doesn't work with many hardware devices, as it's dependent on driver support. Instead, Apple added the `IOSSIOSPEED` ioctl in Mac OS X 10.4, which can set the baud rate to an arbitrary value. As the oldest macOS version supported by Rust is 10.7, it's available on all Mac platforms. This API requires the port to be set into raw mode with `cfmakeraw`, and must be done after every call to `tcsetattr`, as that will reset the baud rate. Additionally, there is no way to retrieve the actual baud rate from the OS. This is therefore the clunkiest API of any platform. diff --git a/src/posix/enumerate.rs b/src/posix/enumerate.rs index 360c0cfc..7cbeb644 100644 --- a/src/posix/enumerate.rs +++ b/src/posix/enumerate.rs @@ -7,7 +7,7 @@ cfg_if! { } cfg_if! { - if #[cfg(any(target_os = "ios", target_os = "macos"))] { + if #[cfg(target_vendor = "apple")] { use core_foundation::base::CFType; use core_foundation::base::TCFType; use core_foundation::dictionary::CFDictionary; @@ -26,22 +26,16 @@ cfg_if! { } } -#[cfg(any( - target_os = "freebsd", - target_os = "ios", - target_os = "linux", - target_os = "macos" -))] +#[cfg(any(target_os = "freebsd", target_os = "linux", target_vendor = "apple"))] use crate::SerialPortType; -#[cfg(any(target_os = "ios", target_os = "linux", target_os = "macos"))] +#[cfg(any(target_os = "linux", target_vendor = "apple"))] use crate::UsbPortInfo; #[cfg(any( target_os = "android", - target_os = "ios", all(target_os = "linux", not(target_env = "musl"), feature = "libudev"), - target_os = "macos", target_os = "netbsd", target_os = "openbsd", + target_vendor = "apple", ))] use crate::{Error, ErrorKind}; use crate::{Result, SerialPortInfo}; @@ -265,7 +259,7 @@ fn parse_modalias(moda: &str) -> Option { }) } -#[cfg(any(target_os = "ios", target_os = "macos"))] +#[cfg(target_vendor = "apple")] fn get_parent_device_by_type( device: io_object_t, parent_type: *const c_char, @@ -292,7 +286,7 @@ fn get_parent_device_by_type( } } -#[cfg(any(target_os = "ios", target_os = "macos"))] +#[cfg(target_vendor = "apple")] #[allow(non_upper_case_globals)] /// Returns a specific property of the given device as an integer. fn get_int_property(device_type: io_registry_entry_t, property: &str) -> Result { @@ -321,7 +315,7 @@ fn get_int_property(device_type: io_registry_entry_t, property: &str) -> Result< )) } -#[cfg(any(target_os = "ios", target_os = "macos"))] +#[cfg(target_vendor = "apple")] /// Returns a specific property of the given device as a string. fn get_string_property(device_type: io_registry_entry_t, property: &str) -> Result { let cf_property = CFString::new(property); @@ -345,7 +339,7 @@ fn get_string_property(device_type: io_registry_entry_t, property: &str) -> Resu .ok_or(Error::new(ErrorKind::Unknown, "Failed to get string value")) } -#[cfg(any(target_os = "ios", target_os = "macos"))] +#[cfg(target_vendor = "apple")] /// Determine the serial port type based on the service object (like that returned by /// `IOIteratorNext`). Specific properties are extracted for USB devices. fn port_type(service: io_object_t) -> SerialPortType { @@ -381,7 +375,7 @@ fn port_type(service: io_object_t) -> SerialPortType { } cfg_if! { - if #[cfg(any(target_os = "ios", target_os = "macos"))] { + if #[cfg(target_vendor = "apple")] { /// Scans the system for serial ports and returns a list of them. /// The `SerialPortInfo` struct contains the name of the port which can be used for opening it. pub fn available_ports() -> Result> { diff --git a/src/posix/ioctl.rs b/src/posix/ioctl.rs index ced1c091..ffefd409 100644 --- a/src/posix/ioctl.rs +++ b/src/posix/ioctl.rs @@ -23,10 +23,9 @@ mod raw { #[cfg(any( target_os = "dragonfly", target_os = "freebsd", - target_os = "ios", - target_os = "macos", target_os = "netbsd", - target_os = "openbsd" + target_os = "openbsd", + target_vendor = "apple", ))] ioctl_read!(fionread, b'f', 127, libc::c_int); @@ -37,10 +36,9 @@ mod raw { #[cfg(any( target_os = "dragonfly", target_os = "freebsd", - target_os = "ios", - target_os = "macos", target_os = "netbsd", - target_os = "openbsd" + target_os = "openbsd", + target_vendor = "apple" ))] ioctl_read!(tiocoutq, b't', 115, libc::c_int); @@ -80,10 +78,10 @@ mod raw { 0x2B, libc::termios2 ); - #[cfg(any(target_os = "ios", target_os = "macos"))] + #[cfg(target_vendor = "apple")] const IOSSIOSPEED: libc::c_ulong = 0x80045402; ioctl_write_ptr_bad!( - #[cfg(any(target_os = "ios", target_os = "macos"))] + #[cfg(target_vendor = "apple")] iossiospeed, IOSSIOSPEED, libc::speed_t @@ -199,7 +197,7 @@ pub fn tcsets2(fd: RawFd, options: &libc::termios2) -> Result<()> { .map_err(|e| e.into()) } -#[cfg(any(target_os = "ios", target_os = "macos"))] +#[cfg(target_vendor = "apple")] pub fn iossiospeed(fd: RawFd, baud_rate: &libc::speed_t) -> Result<()> { unsafe { raw::iossiospeed(fd, baud_rate) } .map(|_| ()) diff --git a/src/posix/termios.rs b/src/posix/termios.rs index 23988218..42594b03 100644 --- a/src/posix/termios.rs +++ b/src/posix/termios.rs @@ -10,8 +10,6 @@ cfg_if! { if #[cfg(any( target_os = "dragonfly", target_os = "freebsd", - target_os = "ios", - target_os = "macos", target_os = "netbsd", target_os = "openbsd", all( @@ -21,7 +19,8 @@ cfg_if! { target_arch = "powerpc", target_arch = "powerpc64" ) - ) + ), + target_vendor = "apple", ))] { pub(crate) type Termios = libc::termios; } else if #[cfg(any( @@ -45,7 +44,7 @@ cfg_if! { // calls in this lib to the IOSSIOSPEED ioctl. So whenever we get this struct, make sure to // reset the input & output baud rates to a safe default. This is accounted for by the // corresponding set_termios that is mac-specific and always calls IOSSIOSPEED. -#[cfg(any(target_os = "ios", target_os = "macos",))] +#[cfg(target_vendor = "apple")] pub(crate) fn get_termios(fd: RawFd) -> Result { use std::mem::MaybeUninit; @@ -96,7 +95,7 @@ pub(crate) fn get_termios(fd: RawFd) -> Result { crate::posix::ioctl::tcgets2(fd) } -#[cfg(any(target_os = "ios", target_os = "macos",))] +#[cfg(target_vendor = "apple")] pub(crate) fn set_termios(fd: RawFd, termios: &libc::termios, baud_rate: u32) -> Result<()> { let res = unsafe { libc::tcsetattr(fd, libc::TCSANOW, termios) }; nix::errno::Errno::result(res)?; diff --git a/src/posix/tty.rs b/src/posix/tty.rs index 678c7c94..66ffb33e 100644 --- a/src/posix/tty.rs +++ b/src/posix/tty.rs @@ -68,7 +68,7 @@ pub struct TTYPort { timeout: Duration, exclusive: bool, port_name: Option, - #[cfg(any(target_os = "ios", target_os = "macos"))] + #[cfg(target_vendor = "apple")] baud_rate: u32, } @@ -169,7 +169,7 @@ impl TTYPort { )); }; - #[cfg(any(target_os = "ios", target_os = "macos"))] + #[cfg(target_vendor = "apple")] if builder.baud_rate > 0 { unsafe { libc::tcflush(fd.0, libc::TCIOFLUSH) }; } @@ -183,11 +183,11 @@ impl TTYPort { termios::set_flow_control(&mut termios, builder.flow_control); termios::set_data_bits(&mut termios, builder.data_bits); termios::set_stop_bits(&mut termios, builder.stop_bits); - #[cfg(not(any(target_os = "ios", target_os = "macos")))] + #[cfg(not(target_vendor = "apple"))] termios::set_baud_rate(&mut termios, builder.baud_rate)?; - #[cfg(any(target_os = "ios", target_os = "macos"))] + #[cfg(target_vendor = "apple")] termios::set_termios(fd.0, &termios, builder.baud_rate)?; - #[cfg(not(any(target_os = "ios", target_os = "macos")))] + #[cfg(not(target_vendor = "apple"))] termios::set_termios(fd.0, &termios)?; // Return the final port object @@ -196,7 +196,7 @@ impl TTYPort { timeout: builder.timeout, exclusive: true, port_name: Some(builder.path.clone()), - #[cfg(any(target_os = "ios", target_os = "macos"))] + #[cfg(target_vendor = "apple")] baud_rate: builder.baud_rate, }; @@ -318,7 +318,7 @@ impl TTYPort { let ptty_name = nix::pty::ptsname_r(&next_pty_fd)?; // Open the slave port - #[cfg(any(target_os = "ios", target_os = "macos"))] + #[cfg(target_vendor = "apple")] let baud_rate = 9600; let fd = nix::fcntl::open( Path::new(&ptty_name), @@ -347,7 +347,7 @@ impl TTYPort { timeout: Duration::from_millis(100), exclusive: true, port_name: Some(ptty_name), - #[cfg(any(target_os = "ios", target_os = "macos"))] + #[cfg(target_vendor = "apple")] baud_rate, }; @@ -359,7 +359,7 @@ impl TTYPort { timeout: Duration::from_millis(100), exclusive: true, port_name: None, - #[cfg(any(target_os = "ios", target_os = "macos"))] + #[cfg(target_vendor = "apple")] baud_rate, }; @@ -396,7 +396,7 @@ impl TTYPort { exclusive: self.exclusive, port_name: self.port_name.clone(), timeout: self.timeout, - #[cfg(any(target_os = "ios", target_os = "macos"))] + #[cfg(target_vendor = "apple")] baud_rate: self.baud_rate, }) } @@ -426,7 +426,7 @@ impl IntoRawFd for TTYPort { } /// Get the baud speed for a port from its file descriptor -#[cfg(any(target_os = "ios", target_os = "macos"))] +#[cfg(target_vendor = "apple")] fn get_termios_speed(fd: RawFd) -> u32 { let mut termios = MaybeUninit::uninit(); let res = unsafe { libc::tcgetattr(fd, termios.as_mut_ptr()) }; @@ -458,7 +458,7 @@ impl FromRawFd for TTYPort { // It's not guaranteed that the baud rate in the `termios` struct is correct, as // setting an arbitrary baud rate via the `iossiospeed` ioctl overrides that value, // but extract that value anyways as a best-guess of the actual baud rate. - #[cfg(any(target_os = "ios", target_os = "macos"))] + #[cfg(target_vendor = "apple")] baud_rate: get_termios_speed(fd), } } @@ -559,7 +559,7 @@ impl SerialPort for TTYPort { /// /// On some platforms this will be the actual device baud rate, which may differ from the /// desired baud rate. - #[cfg(any(target_os = "ios", target_os = "macos"))] + #[cfg(target_vendor = "apple")] fn baud_rate(&self) -> Result { Ok(self.baud_rate) } @@ -695,7 +695,7 @@ impl SerialPort for TTYPort { } // Mac OS needs special logic for setting arbitrary baud rates. - #[cfg(any(target_os = "ios", target_os = "macos"))] + #[cfg(target_vendor = "apple")] fn set_baud_rate(&mut self, baud_rate: u32) -> Result<()> { ioctl::iossiospeed(self.fd, &(baud_rate as libc::speed_t))?; self.baud_rate = baud_rate; @@ -705,36 +705,36 @@ impl SerialPort for TTYPort { fn set_flow_control(&mut self, flow_control: FlowControl) -> Result<()> { let mut termios = termios::get_termios(self.fd)?; termios::set_flow_control(&mut termios, flow_control); - #[cfg(any(target_os = "ios", target_os = "macos"))] + #[cfg(target_vendor = "apple")] return termios::set_termios(self.fd, &termios, self.baud_rate); - #[cfg(not(any(target_os = "ios", target_os = "macos")))] + #[cfg(not(target_vendor = "apple"))] return termios::set_termios(self.fd, &termios); } fn set_parity(&mut self, parity: Parity) -> Result<()> { let mut termios = termios::get_termios(self.fd)?; termios::set_parity(&mut termios, parity); - #[cfg(any(target_os = "ios", target_os = "macos"))] + #[cfg(target_vendor = "apple")] return termios::set_termios(self.fd, &termios, self.baud_rate); - #[cfg(not(any(target_os = "ios", target_os = "macos")))] + #[cfg(not(target_vendor = "apple"))] return termios::set_termios(self.fd, &termios); } fn set_data_bits(&mut self, data_bits: DataBits) -> Result<()> { let mut termios = termios::get_termios(self.fd)?; termios::set_data_bits(&mut termios, data_bits); - #[cfg(any(target_os = "ios", target_os = "macos"))] + #[cfg(target_vendor = "apple")] return termios::set_termios(self.fd, &termios, self.baud_rate); - #[cfg(not(any(target_os = "ios", target_os = "macos")))] + #[cfg(not(target_vendor = "apple"))] return termios::set_termios(self.fd, &termios); } fn set_stop_bits(&mut self, stop_bits: StopBits) -> Result<()> { let mut termios = termios::get_termios(self.fd)?; termios::set_stop_bits(&mut termios, stop_bits); - #[cfg(any(target_os = "ios", target_os = "macos"))] + #[cfg(target_vendor = "apple")] return termios::set_termios(self.fd, &termios, self.baud_rate); - #[cfg(not(any(target_os = "ios", target_os = "macos")))] + #[cfg(not(target_vendor = "apple"))] return termios::set_termios(self.fd, &termios); } diff --git a/tests/test_tty.rs b/tests/test_tty.rs index 46f4ae72..0b242553 100644 --- a/tests/test_tty.rs +++ b/tests/test_tty.rs @@ -91,7 +91,7 @@ fn test_ttyport_timeout() { } #[test] -#[cfg(any(target_os = "ios", target_os = "macos"))] +#[cfg(target_vendor = "apple")] fn test_osx_pty_pair() { #![allow(unused_variables)] let (mut master, slave) = TTYPort::pair().expect("Unable to create ptty pair"); @@ -116,7 +116,7 @@ fn test_osx_pty_pair() { // On Mac this should work (in fact used to in b77768a) but now fails. It's not functionality that // should be required, and the ptys work otherwise. So going to just disable this test instead. #[test] -#[cfg_attr(any(target_os = "ios", target_os = "macos"), ignore)] +#[cfg_attr(target_vendor = "apple", ignore)] fn test_ttyport_set_standard_baud() { // `master` must be used here as Dropping it causes slave to be deleted by the OS. // TODO: Convert this to a statement-level attribute once @@ -133,15 +133,10 @@ fn test_ttyport_set_standard_baud() { assert_eq!(slave.baud_rate().unwrap(), 115_200); } -// On mac this fails because you can't set nonstandard baud rates for these virtual ports #[test] #[cfg_attr( - any( - target_os = "ios", - all(target_os = "linux", target_env = "musl"), - target_os = "macos" - ), - ignore + any(all(target_os = "linux", target_env = "musl"), target_vendor = "apple"), + ignore = "fails on Mac because you can't set nonstandard baud rates for these virtual ports" )] fn test_ttyport_set_nonstandard_baud() { // `master` must be used here as Dropping it causes slave to be deleted by the OS.