From c51e54dbc0b7548f5b5a7874b45103be5c8f92d0 Mon Sep 17 00:00:00 2001 From: Nicolas Dusart Date: Mon, 19 Feb 2018 17:03:06 +0100 Subject: [PATCH 1/2] update serialport to version 2.1 --- Cargo.toml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index a14e571..f2e794d 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -13,10 +13,10 @@ categories = ["asynchronous", "hardware-support"] [dependencies] mio = "0.6" -serialport = "2.0" +serialport = "2.1" [target.'cfg(unix)'.dependencies] -nix = "^0.9" +nix = "0.10" [[example]] name = "serial_printline" From ab8592edae033f3f6089baac2178177701a4d73d Mon Sep 17 00:00:00 2001 From: Nicolas Dusart Date: Sat, 30 Dec 2017 08:32:53 +0100 Subject: [PATCH 2/2] Windows implementation based on mio-named-pipes --- Cargo.toml | 4 ++ examples/serial_printline.rs | 59 ++++++++++++------ src/lib.rs | 12 +++- src/windows.rs | 117 +++++++++++++++++++---------------- src/windows/buffer_pool.rs | 20 ------ 5 files changed, 116 insertions(+), 96 deletions(-) delete mode 100644 src/windows/buffer_pool.rs diff --git a/Cargo.toml b/Cargo.toml index f2e794d..df195b4 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -18,6 +18,10 @@ serialport = "2.1" [target.'cfg(unix)'.dependencies] nix = "0.10" +[target.'cfg(windows)'.dependencies] +winapi = { version = "0.3", features = ["commapi", "handleapi", "winbase"] } +mio-named-pipes = "0.1.5" + [[example]] name = "serial_printline" path = "examples/serial_printline.rs" diff --git a/examples/serial_printline.rs b/examples/serial_printline.rs index aaca365..4e2893e 100644 --- a/examples/serial_printline.rs +++ b/examples/serial_printline.rs @@ -2,16 +2,42 @@ extern crate mio; extern crate mio_serial; -use mio::{Events, Poll, PollOpt, Ready, Token}; +use mio::{Poll, PollOpt, Events, Token, Ready}; +#[cfg(unix)] use mio::unix::UnixReady; +use std::str; use std::io::Read; use std::env; const SERIAL_TOKEN: Token = Token(0); +#[cfg(unix)] +const DEFAULT_TTY: &str = "/dev/ttyUSB0"; +#[cfg(windows)] +const DEFAULT_TTY: &str = "COM1"; + +#[cfg(unix)] +fn ready_of_interest() -> Ready { + Ready::readable() | UnixReady::hup() | UnixReady::error() +} +#[cfg(windows)] +fn ready_of_interest() -> Ready { + Ready::readable() +} + +#[cfg(unix)] +fn is_closed(state: Ready) -> bool { + state.contains(UnixReady::hup() | UnixReady::error()) +} +#[cfg(windows)] +fn is_closed(state: Ready) -> bool { + false +} + pub fn main() { + let mut args = env::args(); - let tty_path = args.nth(1).unwrap_or_else(|| "/dev/ttyUSB0".into()); + let tty_path = args.nth(1).unwrap_or_else(|| DEFAULT_TTY.into()); let poll = Poll::new().unwrap(); let mut events = Events::with_capacity(1024); @@ -22,16 +48,11 @@ pub fn main() { println!("Opening {} at 9600,8N1", tty_path); let mut rx = mio_serial::Serial::from_path(&tty_path, &settings).unwrap(); - // Disable exclusive mode - rx.set_exclusive(false) - .expect("Unable to set serial port into non-exclusive mode."); - - poll.register( - &rx, - SERIAL_TOKEN, - Ready::readable() | UnixReady::hup() | UnixReady::error(), - PollOpt::edge(), - ).unwrap(); + poll.register(&rx, + SERIAL_TOKEN, + ready_of_interest(), + PollOpt::edge()) + .unwrap(); let mut rx_buf = [0u8; 1024]; @@ -47,18 +68,20 @@ pub fn main() { match event.token() { SERIAL_TOKEN => { let ready = event.readiness(); - if ready.contains(UnixReady::hup() | UnixReady::error()) { + if is_closed(ready) { println!("Quitting due to event: {:?}", ready); break 'outer; } if ready.is_readable() { match rx.read(&mut rx_buf) { - Ok(b) => match b { - b if b > 0 => { - println!("{:?}", String::from_utf8_lossy(&rx_buf[..b])) + Ok(b) => { + match b { + b if b > 0 => { + println!("{:?}", String::from_utf8_lossy(&rx_buf[..b])) + } + _ => println!("Read would have blocked."), } - _ => println!("Read would have blocked."), - }, + } Err(e) => println!("Error: {}", e), } } diff --git a/src/lib.rs b/src/lib.rs index 9349066..dd7110c 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -5,9 +5,8 @@ //! **At this time this crate ONLY provides a unix implementation** //! //! ## Links -//! - repo: -//! - docs: -#![cfg(unix)] +//! - repo: https://github.com/berkowski/mio-serial +//! - docs: https://docs.rs/mio-serial #![deny(missing_docs)] extern crate mio; @@ -15,6 +14,10 @@ extern crate serialport; #[cfg(unix)] extern crate nix; +#[cfg(windows)] +extern crate winapi; +#[cfg(windows)] +extern crate mio_named_pipes; // Enums, Structs, and Traits from the serialport crate pub use serialport::{BaudRate, @@ -45,5 +48,8 @@ pub mod windows; #[cfg(unix)] pub use unix::Serial; +#[cfg(windows)] +pub use windows::Serial; + /// A type for results generated by interacting with serial ports. pub type Result = serialport::Result; diff --git a/src/windows.rs b/src/windows.rs index 5757cca..6f61257 100644 --- a/src/windows.rs +++ b/src/windows.rs @@ -1,39 +1,65 @@ -use std::os::windows::prelude::*; -use std::io; -use std::path::Path; -use std::convert::AsRef; - -use serialport::windows::COMport; - -pub fn from_path>(path: T, settings: &::SerialPortSettings) -> io::Result<::Serial> { - -} - -use std::os::windows::prelude::*; +//! Windows impl of mio-enabled serial ports. use std::io::{self, Read, Write}; +use std::mem; use std::path::Path; -use std::convert::AsRef; +use std::ptr; +use std::ffi::OsStr; use std::time::Duration; - -use libc; +use std::os::windows::ffi::OsStrExt; +use std::os::windows::io::FromRawHandle; +use winapi::um::fileapi::*; +use winapi::um::winbase::FILE_FLAG_OVERLAPPED; +use winapi::um::handleapi::INVALID_HANDLE_VALUE; +use winapi::um::winnt::{FILE_ATTRIBUTE_NORMAL, GENERIC_READ, GENERIC_WRITE, HANDLE}; +use serialport::{self, SerialPort, SerialPortSettings}; +use serialport::windows::COMPort; use mio::{Evented, Poll, PollOpt, Ready, Token}; -use mio::unix::EventedFd; +use mio_named_pipes::NamedPipe; -use serialport; -use serialport::windows::COMPort; -use serialport::prelude::*; +/// Windows serial port pub struct Serial { - inner: serialport::windows::COMPort, + inner: COMPort, + pipe: NamedPipe, } impl Serial { + /// Opens a COM port at the specified path pub fn from_path>(path: T, settings: &SerialPortSettings) -> io::Result { - COMPort - .open(path.as_ref(), settings) - .map(|port| ::Serial { inner: port }) - .map_err(|_| Err(io::Error::last_os_error)) + let mut name = Vec::::new(); + + name.extend(OsStr::new("\\\\.\\").encode_wide()); + name.extend(path.as_ref().as_os_str().encode_wide()); + name.push(0); + + let handle = unsafe { + CreateFileW(name.as_ptr(), + GENERIC_READ | GENERIC_WRITE, + 0, + ptr::null_mut(), + OPEN_EXISTING, + FILE_ATTRIBUTE_NORMAL | FILE_FLAG_OVERLAPPED, + 0 as HANDLE) + }; + + if handle != INVALID_HANDLE_VALUE { + let handle = unsafe { mem::transmute(handle) }; + + // Construct NamedPipe and COMPort from Handle + let pipe = unsafe { NamedPipe::from_raw_handle(handle) }; + let mut serial = unsafe { COMPort::from_raw_handle(handle) }; + serial.set_all(settings)?; + + Ok(Serial{ + inner: serial, + pipe: pipe + }) + } else { + Err(io::Error::last_os_error()) + } + } + } impl SerialPort for Serial { @@ -42,6 +68,11 @@ impl SerialPort for Serial { self.inner.settings() } + /// Return the name associated with the serial port, if known. + fn port_name(&self) -> Option { + self.inner.port_name() + } + /// Returns the current baud rate. /// /// This function returns `None` if the baud rate could not be determined. This may occur if @@ -238,41 +269,17 @@ impl SerialPort for Serial { impl Read for Serial { fn read(&mut self, bytes: &mut [u8]) -> io::Result { - match unsafe { - libc::read( - self.as_raw_fd(), - bytes.as_ptr() as *mut libc::c_void, - bytes.len() as libc::size_t, - ) - } { - x if x >= 0 => Ok(x as usize), - _ => Err(io::Error::last_os_error()), - } + self.pipe.read(bytes) } } impl Write for Serial { fn write(&mut self, bytes: &[u8]) -> io::Result { - match unsafe { - libc::write( - self.as_raw_fd(), - bytes.as_ptr() as *const libc::c_void, - bytes.len() as libc::size_t, - ) - } { - x if x >= 0 => Ok(x as usize), - _ => Err(io::Error::last_os_error()), - } + self.pipe.write(bytes) } fn flush(&mut self) -> io::Result<()> { - self.inner.flush() - } -} - -impl AsRawFd for Serial { - fn as_raw_fd(&self) -> RawFd { - self.inner.as_raw_fd() + self.pipe.flush() } } @@ -284,7 +291,7 @@ impl Evented for Serial { interest: Ready, opts: PollOpt, ) -> io::Result<()> { - EventedFd(&self.as_raw_fd()).register(poll, token, interest, opts) + self.pipe.register(poll, token, interest, opts) } fn reregister( @@ -294,10 +301,10 @@ impl Evented for Serial { interest: Ready, opts: PollOpt, ) -> io::Result<()> { - EventedFd(&self.as_raw_fd()).reregister(poll, token, interest, opts) + self.pipe.reregister(poll, token, interest, opts) } fn deregister(&self, poll: &Poll) -> io::Result<()> { - EventedFd(&self.as_raw_fd()).deregister(poll) + self.pipe.deregister(poll) } -} +} \ No newline at end of file diff --git a/src/windows/buffer_pool.rs b/src/windows/buffer_pool.rs deleted file mode 100644 index 8675459..0000000 --- a/src/windows/buffer_pool.rs +++ /dev/null @@ -1,20 +0,0 @@ -pub struct BufferPool { - pool: Vec>, -} - -impl BufferPool { - pub fn new(cap: usize) -> BufferPool { - BufferPool { pool: Vec::with_capacity(cap) } - } - - pub fn get(&mut self, default_cap: usize) -> Vec { - self.pool.pop().unwrap_or_else(|| Vec::with_capacity(default_cap)) - } - - pub fn put(&mut self, mut buf: Vec) { - if self.pool.len() < self.pool.capacity(){ - unsafe { buf.set_len(0); } - self.pool.push(buf); - } - } -}