Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Windows implementation #4

Merged
merged 2 commits into from
Feb 25, 2018
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 6 additions & 2 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -13,10 +13,14 @@ categories = ["asynchronous", "hardware-support"]

[dependencies]
mio = "0.6"
serialport = "2.0"
serialport = "2.1"

[target.'cfg(unix)'.dependencies]
nix = "^0.9"
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"
Expand Down
59 changes: 41 additions & 18 deletions examples/serial_printline.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand All @@ -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];

Expand All @@ -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),
}
}
Expand Down
12 changes: 9 additions & 3 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,16 +5,19 @@
//! **At this time this crate ONLY provides a unix implementation**
//!
//! ## Links
//! - repo: <https://github.com/berkowski/mio-serial>
//! - docs: <https://docs.rs/mio-serial>
#![cfg(unix)]
//! - repo: https://github.com/berkowski/mio-serial
//! - docs: https://docs.rs/mio-serial
#![deny(missing_docs)]

extern crate mio;
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,
Expand Down Expand Up @@ -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<T> = serialport::Result<T>;
117 changes: 62 additions & 55 deletions src/windows.rs
Original file line number Diff line number Diff line change
@@ -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<T: AsRef<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<T: AsRef<Path>>(path: T, settings: &SerialPortSettings) -> io::Result<Self> {
COMPort
.open(path.as_ref(), settings)
.map(|port| ::Serial { inner: port })
.map_err(|_| Err(io::Error::last_os_error))
let mut name = Vec::<u16>::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 {
Expand All @@ -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<String> {
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
Expand Down Expand Up @@ -238,41 +269,17 @@ impl SerialPort for Serial {

impl Read for Serial {
fn read(&mut self, bytes: &mut [u8]) -> io::Result<usize> {
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<usize> {
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()
}
}

Expand All @@ -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(
Expand All @@ -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)
}
}
}
20 changes: 0 additions & 20 deletions src/windows/buffer_pool.rs

This file was deleted.