From cad513b703ba490ee7dffc6774e8b9ce37748e8e Mon Sep 17 00:00:00 2001 From: druid <> Date: Sun, 11 Jan 2009 05:35:23 +0000 Subject: [PATCH] MSF fork of the RubyForge ruby-serialport library git-svn-id: file:///home/svn/framework3/trunk@6117 4d416f70-5f16-0410-b530-b9f4589650da --- external/serialport/CHANGELOG | 14 + external/serialport/MANIFEST | 10 + external/serialport/Makefile | 149 +++ external/serialport/README.msf | 61 ++ external/serialport/README.orig | 152 +++ external/serialport/debian/changelog | 39 + external/serialport/debian/control | 13 + external/serialport/debian/copyright | 18 + external/serialport/debian/rules | 83 ++ external/serialport/extconf.rb | 13 + external/serialport/mkmf.log | 20 + external/serialport/serialport.c | 1487 ++++++++++++++++++++++++++ external/serialport/test/miniterm.rb | 25 + 13 files changed, 2084 insertions(+) create mode 100644 external/serialport/CHANGELOG create mode 100644 external/serialport/MANIFEST create mode 100644 external/serialport/Makefile create mode 100644 external/serialport/README.msf create mode 100644 external/serialport/README.orig create mode 100644 external/serialport/debian/changelog create mode 100644 external/serialport/debian/control create mode 100644 external/serialport/debian/copyright create mode 100644 external/serialport/debian/rules create mode 100644 external/serialport/extconf.rb create mode 100644 external/serialport/mkmf.log create mode 100644 external/serialport/serialport.c create mode 100644 external/serialport/test/miniterm.rb diff --git a/external/serialport/CHANGELOG b/external/serialport/CHANGELOG new file mode 100644 index 0000000000..321d62a543 --- /dev/null +++ b/external/serialport/CHANGELOG @@ -0,0 +1,14 @@ +0.6 => 12/02/2003: Windows support + Get/set modem parameters + Read and write timeouts + Open method + +0.5 => 25/10/2002: Cygwin support + +0.4 => 19/09/2002: Added more serial ports (total of 8 ports) + +0.3 => 15/03/2002: Damn, another bug found + +0.2 => 14/03/2002: A bug fixed (read() was not blocking) + +0.1 => 14/03/2002: First release diff --git a/external/serialport/MANIFEST b/external/serialport/MANIFEST new file mode 100644 index 0000000000..37c83cfd01 --- /dev/null +++ b/external/serialport/MANIFEST @@ -0,0 +1,10 @@ +test/miniterm.rb +README +CHANGELOG +debian/control +debian/rules +debian/changelog +debian/copyright +extconf.rb +serialport.c +.cvsignore diff --git a/external/serialport/Makefile b/external/serialport/Makefile new file mode 100644 index 0000000000..69a41a1f0b --- /dev/null +++ b/external/serialport/Makefile @@ -0,0 +1,149 @@ + +SHELL = /bin/sh + +#### Start of system configuration section. #### + +srcdir = . +topdir = /usr/lib/ruby/1.8/i486-linux +hdrdir = $(topdir) +VPATH = $(srcdir):$(topdir):$(hdrdir) +prefix = $(DESTDIR)/usr +exec_prefix = $(prefix) +sitedir = $(prefix)/lib/ruby/site_ruby +rubylibdir = $(libdir)/ruby/$(ruby_version) +docdir = $(datarootdir)/doc/$(PACKAGE) +dvidir = $(docdir) +datarootdir = $(prefix)/share +archdir = $(rubylibdir)/$(arch) +sbindir = $(exec_prefix)/sbin +psdir = $(docdir) +localedir = $(datarootdir)/locale +htmldir = $(docdir) +datadir = $(DESTDIR)/usr/share +includedir = $(prefix)/include +infodir = $(datarootdir)/info +sysconfdir = $(prefix)/etc +mandir = $(DESTDIR)/usr/man +libdir = $(exec_prefix)/lib +sharedstatedir = $(prefix)/com +oldincludedir = $(DESTDIR)/usr/include +pdfdir = $(docdir) +sitearchdir = $(sitelibdir)/$(sitearch) +bindir = $(exec_prefix)/bin +localstatedir = $(prefix)/var +sitelibdir = $(sitedir)/$(ruby_version) +libexecdir = $(exec_prefix)/libexec + +CC = gcc +LIBRUBY = $(LIBRUBY_SO) +LIBRUBY_A = lib$(RUBY_SO_NAME)-static.a +LIBRUBYARG_SHARED = -Wl,-R -Wl,$(libdir) -L$(libdir) -L. -l$(RUBY_SO_NAME) +LIBRUBYARG_STATIC = -l$(RUBY_SO_NAME)-static + +RUBY_EXTCONF_H = +CFLAGS = -fPIC -O2 -march=i486 -mtune=i686 -fPIC -Dlinux +INCFLAGS = -I. -I$(topdir) -I$(hdrdir) -I$(srcdir) +CPPFLAGS = -DHAVE_TERMIOS_H -DHAVE_UNISTD_H +CXXFLAGS = $(CFLAGS) +DLDFLAGS = -rdynamic -Wl,-export-dynamic +LDSHARED = $(CC) -shared +AR = ar +EXEEXT = + +RUBY_INSTALL_NAME = ruby +RUBY_SO_NAME = ruby +arch = i486-linux +sitearch = i486-linux +ruby_version = 1.8 +ruby = /usr/bin/ruby +RUBY = $(ruby) +RM = rm -f +MAKEDIRS = mkdir -p +INSTALL = /usr/bin/ginstall -c +INSTALL_PROG = $(INSTALL) -m 0755 +INSTALL_DATA = $(INSTALL) -m 644 +COPY = cp + +#### End of system configuration section. #### + +preload = + +libpath = $(libdir) +LIBPATH = -L'$(libdir)' -Wl,-R'$(libdir)' +DEFFILE = + +CLEANFILES = +DISTCLEANFILES = + +extout = +extout_prefix = +target_prefix = +LOCAL_LIBS = +LIBS = $(LIBRUBYARG_SHARED) -ldl -lcrypt -lm -lc +SRCS = serialport.c +OBJS = serialport.o +TARGET = serialport +DLLIB = $(TARGET).so +EXTSTATIC = +STATIC_LIB = + +RUBYCOMMONDIR = $(sitedir)$(target_prefix) +RUBYLIBDIR = $(sitelibdir)$(target_prefix) +RUBYARCHDIR = $(sitearchdir)$(target_prefix) + +TARGET_SO = $(DLLIB) +CLEANLIBS = $(TARGET).so $(TARGET).il? $(TARGET).tds $(TARGET).map +CLEANOBJS = *.o *.a *.s[ol] *.pdb *.exp *.bak + +all: $(DLLIB) +static: $(STATIC_LIB) + +clean: + @-$(RM) $(CLEANLIBS) $(CLEANOBJS) $(CLEANFILES) + +distclean: clean + @-$(RM) Makefile $(RUBY_EXTCONF_H) conftest.* mkmf.log + @-$(RM) core ruby$(EXEEXT) *~ $(DISTCLEANFILES) + +realclean: distclean +install: install-so install-rb + +install-so: $(RUBYARCHDIR) +install-so: $(RUBYARCHDIR)/$(DLLIB) +$(RUBYARCHDIR)/$(DLLIB): $(DLLIB) + $(INSTALL_PROG) $(DLLIB) $(RUBYARCHDIR) +install-rb: pre-install-rb install-rb-default +install-rb-default: pre-install-rb-default +pre-install-rb: Makefile +pre-install-rb-default: Makefile +$(RUBYARCHDIR): + $(MAKEDIRS) $@ + +site-install: site-install-so site-install-rb +site-install-so: install-so +site-install-rb: install-rb + +.SUFFIXES: .c .m .cc .cxx .cpp .C .o + +.cc.o: + $(CXX) $(INCFLAGS) $(CPPFLAGS) $(CXXFLAGS) -c $< + +.cxx.o: + $(CXX) $(INCFLAGS) $(CPPFLAGS) $(CXXFLAGS) -c $< + +.cpp.o: + $(CXX) $(INCFLAGS) $(CPPFLAGS) $(CXXFLAGS) -c $< + +.C.o: + $(CXX) $(INCFLAGS) $(CPPFLAGS) $(CXXFLAGS) -c $< + +.c.o: + $(CC) $(INCFLAGS) $(CPPFLAGS) $(CFLAGS) -c $< + +$(DLLIB): $(OBJS) + @-$(RM) $@ + $(LDSHARED) $(DLDFLAGS) $(LIBPATH) -o $@ $(OBJS) $(LOCAL_LIBS) $(LIBS) + + + +$(OBJS): ruby.h defines.h diff --git a/external/serialport/README.msf b/external/serialport/README.msf new file mode 100644 index 0000000000..5dc7805979 --- /dev/null +++ b/external/serialport/README.msf @@ -0,0 +1,61 @@ +Due to the ruby-serialport project provided by RubyForge.org apparently +being unmaintained, this is a MSF-specific fork of that project. This +version has been modified by both MSF developers as well as by applying +a patch found submitted to the RubyForge project but never included in +the distribution provided there. The versioning scheme has been updated +from the format X.y, to X.Y.z-msf. The text accompanying the applied +patch can be found below. + +Please see the README.orig file for the original project's README, which +includes buid and install instructions as well as documentation of the +SerialPort Class API. + + -- I)ruid + +######################################################################## +Applied Patches: + +======================================================================== +[#878] Six new methods (implemented for the posix flavor only) for the +SerialPort class + +I had to use the SerialPort class to communicate chunks of binary data +through the serial port, and i needed to set some of the low-level +flags onto the serial fd using fcntl() and tcsetattr(), so I implemented +some nifty methods and added them to the class. + +The six new methods are: + +.nonblock= and .nonblock() -> Manipulate the O_NONBLOCK descriptor flag +.input_type= and .input_type() -> Manipulate the ICANON/RAW input flags +.output_type= and .output_type() -> Manipulate the OPOST/RAW output flags + +Also two new constants were added: + +SerialPort::PROCESSED -> Identifies the CANONICAL/PROCESSED I/O +SerialPort::RAW -> Identifies the RAW/RAW I/O + +Example usage: + + def open_port + ret = SerialPort.new @device + + ret.baud = @port_baud + ret.data_bits = @port_data_bits + ret.stop_bits = @port_stop_bits + ret.parity = SerialPort::NONE + ret.flow_control = SerialPort::HARD + ret.input_type = SerialPort::RAW + ret.output_type = SerialPort::RAW + ret.read_timeout = -1 + ret.nonblock = false + ret.dtr = 1 + + ret + end + +This was the best setup for modem communications I could achieve, for +binary and string I/O, in a multithreaded application. Obviously YMMV. + +-void +void@core-dumped.info diff --git a/external/serialport/README.orig b/external/serialport/README.orig new file mode 100644 index 0000000000..fd565edeb0 --- /dev/null +++ b/external/serialport/README.orig @@ -0,0 +1,152 @@ +-----Ruby/SerialPort----- + +-- Description -- + +Ruby/SerialPort is a Ruby library that provides a class for using +RS-232 serial ports. This class also contains low-level functions to +check and set the current state of the signals on the line. + +The native Windows version of this library supports Microsoft's Visual C++ +and Borland's C++ compilers. + +-- Installation -- + +$ ruby extconf.rb +$ make +$ make install + + +-- Testing -- + +* test/miniterm.rb + +Ruby's copy of miniterm.c ! + + +-- API -- + + **** Class SerialPort, Parent IO **** + + ** Class constants ** + + VERSION -> aString (this release is "0.6") + NONE, HARD, SOFT, SPACE, MARK, EVEN, ODD -> anInteger + + ** Class methods ** + + * new(port_num [, modem_parameters]) -> aSerialPort + * open(port_num [, modem_parameters]) -> aSerialPort + * open(port_num [, modem_parameters]) {|aSerialPort| block} -> nil + + port_num -> anInteger: port number, 0 for first port which is + "/dev/ttyS0" on GNU/Linux and "COM1" on Windows, + or aString: file name of the device (example: "/dev/ttyS2") + + Optional modem_parameters: + + baudrate -> anInteger: from 50 to 256000, depends on platform. + + databits -> anInteger: from 5 to 8 (4 is allowed on Windows) + + stopbits -> anInteger: 1 or 2 (1.5 is not supported) + + parity -> anInteger: SerialPort::NONE, SerialPort::EVEN, + SerialPort::ODD, SerialPort::MARK, SerialPort::SPACE + (MARK and SPACE are not supported on Posix) + + Raise an argError on bad argument. + + SerialPort.new and SerialPort.open without a block return an + instance of SerialPort. SerialPort.open with a block passes + a SerialPort to the block and closes it when the block exits + (like File.open). + + + ** Instance methods ** + + * modem_params() -> aHash + * modem_params=(aHash) -> self + * get_modem_params() -> aHash + * set_modem_params(aHash) -> self + * set_modem_params(baudrate [, databits [, stopbits [, parity]]]) + + Get and set the modem parameters. Hash keys are "baud", "data_bits", + "stop_bits", and "parity" (see above). + + Parameters not present in the hash or set to nil remain unchanged. + Default parameter values for the set_modem_params method are: + databits = 8, stopbits = 1, parity = (databits == 8 ? + SerialPort::NONE : SerialPort::EVEN). + + * baud() -> anInteger + * baud=(anInteger) -> self + * data_bits() -> 4, 5, 6, 7, or 8 + * data_bits=(anInteger) -> self + * stop_bits() -> 1 or 2 + * stop_bits=(anInteger) -> self + * parity() -> anInteger: SerialPort::NONE, SerialPort::EVEN, + SerialPort::ODD, SerialPort::MARK, or SerialPort::SPACE + * parity=(anInteger) -> self + + Get and set the corresponding modem parameter. + + * flow_control() -> anInteger + * flow_control=(anInteger) -> self + + Get and set the flow control: SerialPort::NONE, SerialPort::HARD, + SerialPort::SOFT, or (SerialPort::HARD | SerialPort::SOFT). + + Note: SerialPort::HARD mode is not supported on all platforms. + SerialPort::HARD uses RTS/CTS handshaking; DSR/DTR is not + supported. + + * read_timeout() -> anInteger + * read_timeout=(anInteger) -> self + * write_timeout() -> anInteger + * write_timeout=(anInteger) -> self + + Get and set timeout values (in milliseconds) for reading and writing. + A negative read timeout will return all the available data without + waiting, a zero read timeout will not return until at least one + byte is available, and a positive read timeout returns when the + requested number of bytes is available or the interval between the + arrival of two bytes exceeds the timeout value. + + Note: Read timeouts don't mix well with multi-threading. + + Note: Under Posix, write timeouts are not implemented. + + * break(time) -> nil + + Send a break for the given time. + + time -> anInteger: tenths-of-a-second for the break. + Note: Under Posix, this value is very approximate. + + * signals() -> anHash + + Return a hash with the state of each line status bit. Keys are + "rts", "dtr", "cts", "dsr", "dcd", and "ri". + + Note: Under Windows, the rts and dtr values are not included. + + * rts() + * dtr() + * cts() + * dsr() + * dcd() + * ri() -> 0 or 1 + + * rts=(0 or 1) + * dtr=(0 or 1) -> self + + Get and set the corresponding line status bit. + + Note: Under Windows, rts() and dtr() are not implemented. + +-- License -- + +GPL + +Guillaume Pierronnet +Alan Stern diff --git a/external/serialport/debian/changelog b/external/serialport/debian/changelog new file mode 100644 index 0000000000..957b789bf1 --- /dev/null +++ b/external/serialport/debian/changelog @@ -0,0 +1,39 @@ +ruby-serialport (0.6.1) unstable; urgency=low + + * Added nonblock= and nonblock() + * Added input_type= and input_type() + * Added output_type= and output_type() + * Re-indented code + + -- Marcello Barnaba Wed, 8 Sep 2004 17:18:25 +0200 + +ruby-serialport (0.6) unstable; urgency=low + + * Windows support + * Get/set modem parameters + * Read and write timeouts + * Open method + + -- Guillaume Pierronnet Thu, 13 Feb 2003 20:59:37 +0100 + +ruby-serialport (0.5-1) unstable; urgency=low + + * Cygwin support + + -- Guillaume Pierronnet Fri, 25 Oct 2002 13:48:44 +0200 + +ruby-serialport (0.4-1) unstable; urgency=low + + * Modified to support 8 serial ports + + -- Guillaume Pierronnet Fri, 20 Sep 2002 15:26:20 +0200 + +ruby-serialport (0.3-1) unstable; urgency=low + + * Initial Release. + + -- Guillaume Pierronnet Mon, 21 Sep 2001 11:19:36 +0900 + +Local variables: +mode: debian-changelog +End: diff --git a/external/serialport/debian/control b/external/serialport/debian/control new file mode 100644 index 0000000000..e3d83fcfd1 --- /dev/null +++ b/external/serialport/debian/control @@ -0,0 +1,13 @@ +Source: ruby-serialport +Section: interpreters +Priority: optional +Maintainer: Guillaume Pierronnet +Standards-Version: 3.1.1 +Build-Depends: debhelper (>= 2.1.2), ruby (>= 1.8-1), ruby1.8-dev (>= 1.8-1) + +Package: ruby-serialport +Architecture: any +Depends: ${shlibs:Depends}, ruby (>= 1.8) +Description: serial port module for Ruby + serial port module for ruby + diff --git a/external/serialport/debian/copyright b/external/serialport/debian/copyright new file mode 100644 index 0000000000..6998eb0cca --- /dev/null +++ b/external/serialport/debian/copyright @@ -0,0 +1,18 @@ +This package was debianized by Guillaume Pierronnet +Mon, 25 Mar 2002 22:19:36 +0100. + +It was downloaded from http://davedd.free.fr/ruby-serialport/ + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. diff --git a/external/serialport/debian/rules b/external/serialport/debian/rules new file mode 100644 index 0000000000..12b2d21285 --- /dev/null +++ b/external/serialport/debian/rules @@ -0,0 +1,83 @@ +#!/usr/bin/make -f +# Sample debian/rules that uses debhelper. +# GNU copyright 1997 to 1999 by Joey Hess. + +# Uncomment this to turn on verbose mode. +export DH_VERBOSE=1 + +# This is the debhelper compatability version to use. +export DH_COMPAT=2 + +PACKAGE_NAME=ruby-serialport +#GNU_ARCH=$(shell ruby -e 'puts RUBY_PLATFORM') + +#RBV=1.6 + +build: build-stamp +build-stamp: + dh_testdir + + ruby extconf.rb + $(MAKE) + + touch build-stamp + +clean: + dh_testdir + dh_testroot + rm -f build-stamp install-stamp + + # Add here commands to clean up after the build process. + -$(MAKE) distclean + dh_clean + +install: build + dh_testdir + dh_testroot + dh_clean + dh_installdirs + + # Add here commands to install the package into debian/tmp. + $(MAKE) install DESTDIR=`pwd`/debian/$(PACKAGE_NAME) +# mkdir -p debian/tmp/$(RBV)/$(GNU_ARCH) +# cp serialport.so debian/$(PACKAGE_NAME)/usr/lib/ruby/$(RBV)/$(GNU_ARCH)/ + + touch install-stamp + +# Build architecture-independent files here. +binary-indep: build install +# We have nothing to do by default. + +# Build architecture-dependent files here. +binary-arch: build install +# dh_testversion + dh_testdir + dh_testroot +# dh_installdebconf + dh_installdocs README* + dh_installexamples +# dh_installmenu +# dh_installemacsen +# dh_installpam +# dh_installinit +# dh_installcron +# dh_installmanpages +# dh_installinfo +# dh_undocumented + dh_installchangelogs + dh_link + dh_strip + dh_compress + dh_fixperms + # You may want to make some executables suid here. +# dh_suidregister + dh_makeshlibs + dh_installdeb +# dh_perl + dh_shlibdeps + dh_gencontrol + dh_md5sums + dh_builddeb + +binary: binary-indep binary-arch +.PHONY: build clean binary-indep binary-arch binary install diff --git a/external/serialport/extconf.rb b/external/serialport/extconf.rb new file mode 100644 index 0000000000..511c0ec114 --- /dev/null +++ b/external/serialport/extconf.rb @@ -0,0 +1,13 @@ +require 'mkmf' + +printf("checking for OS... ") +STDOUT.flush +os = /-([a-z]+)/.match(RUBY_PLATFORM)[1] +puts(os) +$CFLAGS += " -D#{os}" + +if !(os == 'mswin' or os == 'bccwin') + exit(1) if not have_header("termios.h") or not have_header("unistd.h") +end + +create_makefile("serialport") diff --git a/external/serialport/mkmf.log b/external/serialport/mkmf.log new file mode 100644 index 0000000000..c7bfbf030e --- /dev/null +++ b/external/serialport/mkmf.log @@ -0,0 +1,20 @@ +have_header: checking for termios.h... -------------------- yes + +"gcc -E -I. -I/usr/lib/ruby/1.8/i486-linux -I. -O2 -march=i486 -mtune=i686 -fPIC -Dlinux conftest.c -o conftest.i" +checked program was: +/* begin */ +1: #include +/* end */ + +-------------------- + +have_header: checking for unistd.h... -------------------- yes + +"gcc -E -I. -I/usr/lib/ruby/1.8/i486-linux -I. -O2 -march=i486 -mtune=i686 -fPIC -Dlinux conftest.c -o conftest.i" +checked program was: +/* begin */ +1: #include +/* end */ + +-------------------- + diff --git a/external/serialport/serialport.c b/external/serialport/serialport.c new file mode 100644 index 0000000000..39f2e11c2e --- /dev/null +++ b/external/serialport/serialport.c @@ -0,0 +1,1487 @@ +/* Ruby/SerialPort $Id: serialport.c,v 1.1.1.1 2004/05/25 20:41:09 vjt Exp $ + * Guillaume Pierronnet + * Alan Stern + * + * This code is hereby licensed for public consumption under either the + * GNU GPL v2 or greater. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * + * For documentation on serial programming, see the excellent: + * "Serial Programming Guide for POSIX Operating Systems" + * written Michael R. Sweet. + * http://www.easysw.com/~mike/serial/ + */ + +#define VERSION "0.6.1-msf" + +#include /* ruby inclusion */ +#include /* ruby io inclusion */ + +struct modem_params { + int data_rate; + int data_bits; + int stop_bits; + int parity; + }; + +struct line_signals { + int rts; + int dtr; + int cts; + int dsr; + int dcd; + int ri; + }; + +VALUE cSerialPort; /* serial port class */ + +static VALUE sBaud, sDataBits, sStopBits, sParity; /* strings */ +static VALUE sRts, sDtr, sCts, sDsr, sDcd, sRi; + + +#if defined(mswin) || defined(bccwin) + + +#include /* Standard input/output definitions */ +#include /* Low-level I/O definitions */ +#include /* File control definitions */ +#include /* Windows standard function definitions */ + +#define NONE 0 +#define HARD 1 +#define SOFT 2 + +#define SPACE SPACEPARITY +#define MARK MARKPARITY +#define EVEN EVENPARITY +#define ODD ODDPARITY + +static char sGetCommState[] = "GetCommState"; +static char sSetCommState[] = "SetCommState"; +static char sGetCommTimeouts[] = "GetCommTimeouts"; +static char sSetCommTimeouts[] = "SetCommTimeouts"; + + +static HANDLE sp_get_handle(obj) + VALUE obj; +{ + OpenFile *fptr; + + GetOpenFile(obj, fptr); + return (HANDLE) _get_osfhandle(fileno(fptr->f)); +} + +static VALUE sp_create(class, _port) + VALUE class, _port; +{ + OpenFile *fp; + int fd; + HANDLE fh; + int num_port; + char *port; + char *ports[] = { + "COM1", "COM2", "COM3", "COM4", + "COM5", "COM6", "COM7", "COM8" + }; + DCB dcb; + + NEWOBJ(sp, struct RFile); + rb_secure(4); + OBJSETUP(sp, class, T_FILE); + MakeOpenFile(sp, fp); + + switch(TYPE(_port)) { + case T_FIXNUM: + num_port = FIX2INT(_port); + if (num_port < 0 || num_port > sizeof(ports) / sizeof(ports[0])) + rb_raise(rb_eArgError, "illegal port number"); + port = ports[num_port]; + break; + + case T_STRING: + Check_SafeStr(_port); + port = RSTRING(_port)->ptr; + break; + + default: + rb_raise(rb_eTypeError, "wrong argument type"); + break; + } + + fd = open(port, O_BINARY | O_RDWR); + if (fd == -1) + rb_sys_fail(port); + fh = (HANDLE) _get_osfhandle(fd); + if (SetupComm(fh, 1024, 1024) == 0) { + close(fd); + rb_raise(rb_eArgError, "not a serial port"); + } + + dcb.DCBlength = sizeof(dcb); + if (GetCommState(fh, &dcb) == 0) { + close(fd); + rb_sys_fail(sGetCommState); + } + dcb.fBinary = TRUE; + dcb.fParity = FALSE; + dcb.fOutxDsrFlow = FALSE; + dcb.fDtrControl = DTR_CONTROL_ENABLE; + dcb.fDsrSensitivity = FALSE; + dcb.fTXContinueOnXoff = FALSE; + dcb.fErrorChar = FALSE; + dcb.fNull = FALSE; + dcb.fAbortOnError = FALSE; + dcb.XonChar = 17; + dcb.XoffChar = 19; + if (SetCommState(fh, &dcb) == 0) { + close(fd); + rb_sys_fail(sSetCommState); + } + + fp->f = rb_fdopen(fd, "rb+"); + fp->mode = FMODE_READWRITE | FMODE_BINMODE | FMODE_SYNC; + return (VALUE) sp; +} + +static VALUE sp_set_modem_params(argc, argv, self) + int argc; + VALUE *argv, self; +{ + HANDLE fh; + DCB dcb; + VALUE _data_rate = 0, _data_bits = 0, _parity = NONE, _stop_bits = 0; + int use_hash = 0; + int data_rate, data_bits, parity; + + if (argc == 0) + return self; + if (argc == 1 && T_HASH == TYPE(argv[0])) { + use_hash = 1; + _data_rate = rb_hash_aref(argv[0], sBaud); + _data_bits = rb_hash_aref(argv[0], sDataBits); + _stop_bits = rb_hash_aref(argv[0], sStopBits); + _parity = rb_hash_aref(argv[0], sParity); + } + + fh = sp_get_handle(self); + dcb.DCBlength = sizeof(dcb); + if (GetCommState(fh, &dcb) == 0) + rb_sys_fail(sGetCommState); + + if (!use_hash) + _data_rate = argv[0]; + if (NIL_P(_data_rate)) + goto SkipDataRate; + Check_Type(_data_rate, T_FIXNUM); + + data_rate = FIX2INT(_data_rate); + switch (data_rate) { + case 110: + case 300: + case 600: + case 1200: + case 2400: + case 4800: + case 9600: + case 14400: + case 19200: + case 38400: + case 56000: + case 57600: + case 115200: + case 128000: + case 256000: + dcb.BaudRate = data_rate; + break; + + default: + rb_raise(rb_eArgError, "unknown baud rate"); + break; + } +SkipDataRate: + + if (!use_hash) + _data_bits = (argc >= 2 ? argv[1] : INT2FIX(8)); + if (NIL_P(_data_bits)) + goto SkipDataBits; + Check_Type(_data_bits, T_FIXNUM); + + data_bits = FIX2INT(_data_bits); + if (4 <= data_bits && data_bits <= 8) + dcb.ByteSize = data_bits; + else + rb_raise(rb_eArgError, "unknown character size"); +SkipDataBits: + + if (!use_hash) + _stop_bits = (argc >= 3 ? argv[2] : INT2FIX(1)); + if (NIL_P(_stop_bits)) + goto SkipStopBits; + Check_Type(_stop_bits, T_FIXNUM); + + switch (FIX2INT(_stop_bits)) { + case 1: + dcb.StopBits = ONESTOPBIT; + break; + case 2: + dcb.StopBits = TWOSTOPBITS; + break; + default: + rb_raise(rb_eArgError, "unknown number of stop bits"); + break; + } +SkipStopBits: + + if (!use_hash) + _parity = (argc >= 4 ? argv[3] : (dcb.ByteSize == 8 ? + INT2FIX(NOPARITY) : INT2FIX(EVENPARITY))); + if (NIL_P(_parity)) + goto SkipParity; + Check_Type(_parity, T_FIXNUM); + + parity = FIX2INT(_parity); + switch (parity) { + case EVENPARITY: + case ODDPARITY: + case MARKPARITY: + case SPACEPARITY: + case NOPARITY: + dcb.Parity = parity; + break; + + default: + rb_raise(rb_eArgError, "unknown parity"); + break; + } +SkipParity: + + if (SetCommState(fh, &dcb) == 0) + rb_sys_fail(sSetCommState); + return self; +} + +static void get_modem_params(self, mp) + VALUE self; + struct modem_params *mp; +{ + HANDLE fh; + DCB dcb; + + fh = sp_get_handle(self); + dcb.DCBlength = sizeof(dcb); + if (GetCommState(fh, &dcb) == 0) + rb_sys_fail(sGetCommState); + + mp->data_rate = dcb.BaudRate; + mp->data_bits = dcb.ByteSize; + mp->stop_bits = (dcb.StopBits == ONESTOPBIT ? 1 : 2); + mp->parity = dcb.Parity; +} + +static VALUE sp_set_flow_control(self, val) + VALUE self, val; +{ + HANDLE fh; + int flowc; + DCB dcb; + + Check_Type(val, T_FIXNUM); + + fh = sp_get_handle(self); + dcb.DCBlength = sizeof(dcb); + if (GetCommState(fh, &dcb) == 0) + rb_sys_fail(sGetCommState); + + flowc = FIX2INT(val); + if (flowc & HARD) { + dcb.fRtsControl = RTS_CONTROL_HANDSHAKE; + dcb.fOutxCtsFlow = TRUE; + } else { + dcb.fRtsControl = RTS_CONTROL_ENABLE; + dcb.fOutxCtsFlow = FALSE; + } + if (flowc & SOFT) + dcb.fOutX = dcb.fInX = TRUE; + else + dcb.fOutX = dcb.fInX = FALSE; + + if (SetCommState(fh, &dcb) == 0) + rb_sys_fail(sSetCommState); + return self; +} + +static VALUE sp_get_flow_control(self) + VALUE self; +{ + HANDLE fh; + int ret; + DCB dcb; + + fh = sp_get_handle(self); + dcb.DCBlength = sizeof(dcb); + if (GetCommState(fh, &dcb) == 0) + rb_sys_fail(sGetCommState); + + ret = 0; + if (dcb.fOutxCtsFlow) + ret += HARD; + if (dcb.fOutX) + ret += SOFT; + + return INT2FIX(ret); +} + +static VALUE sp_set_input_type(self, val) +{ + rb_notimplement(); + return self; +} + +static VALUE sp_get_input_type(self) +{ + rb_notimplement(); + return self; +} + +static VALUE sp_set_output_type(self, val) +{ + rb_notimplement(); + return self; +} + +static VALUE sp_get_output_type(self) +{ + rb_notimplement(); + return self; +} + +static VALUE sp_set_nonblock(self, val) +{ + rb_notimplement(); + return self; +} + +static VALUE sp_get_nonblock(self) +{ + rb_notimplement(); + return self; +} + +static VALUE sp_set_read_timeout(self, val) + VALUE self, val; +{ + int timeout; + HANDLE fh; + COMMTIMEOUTS ctout; + + Check_Type(val, T_FIXNUM); + timeout = FIX2INT(val); + + fh = sp_get_handle(self); + if (GetCommTimeouts(fh, &ctout) == 0) + rb_sys_fail(sGetCommTimeouts); + + if (timeout < 0) { + ctout.ReadIntervalTimeout = MAXDWORD; + ctout.ReadTotalTimeoutMultiplier = 0; + ctout.ReadTotalTimeoutConstant = 0; + } else if (timeout == 0) { + ctout.ReadIntervalTimeout = MAXDWORD; + ctout.ReadTotalTimeoutMultiplier = MAXDWORD; + ctout.ReadTotalTimeoutConstant = MAXDWORD - 1; + } else { + ctout.ReadIntervalTimeout = timeout; + ctout.ReadTotalTimeoutMultiplier = 0; + ctout.ReadTotalTimeoutConstant = timeout; + } + + if (SetCommTimeouts(fh, &ctout) == 0) + rb_sys_fail(sSetCommTimeouts); + return self; +} + +static VALUE sp_get_read_timeout(self) + VALUE self; +{ + HANDLE fh; + COMMTIMEOUTS ctout; + + fh = sp_get_handle(self); + if (GetCommTimeouts(fh, &ctout) == 0) + rb_sys_fail(sGetCommTimeouts); + switch (ctout.ReadTotalTimeoutConstant) { + case 0: + return INT2FIX(-1); + case MAXDWORD: + return INT2FIX(0); + } + return INT2FIX(ctout.ReadTotalTimeoutConstant); +} + +static VALUE sp_set_write_timeout(self, val) + VALUE self, val; +{ + int timeout; + HANDLE fh; + COMMTIMEOUTS ctout; + + Check_Type(val, T_FIXNUM); + timeout = FIX2INT(val); + + fh = sp_get_handle(self); + if (GetCommTimeouts(fh, &ctout) == 0) + rb_sys_fail(sGetCommTimeouts); + + if (timeout <= 0) { + ctout.WriteTotalTimeoutMultiplier = 0; + ctout.WriteTotalTimeoutConstant = 0; + } else { + ctout.WriteTotalTimeoutMultiplier = timeout; + ctout.WriteTotalTimeoutConstant = 0; + } + + if (SetCommTimeouts(fh, &ctout) == 0) + rb_sys_fail(sSetCommTimeouts); + return self; +} + +static VALUE sp_get_write_timeout(self) + VALUE self; +{ + HANDLE fh; + COMMTIMEOUTS ctout; + + fh = sp_get_handle(self); + if (GetCommTimeouts(fh, &ctout) == 0) + rb_sys_fail(sGetCommTimeouts); + return INT2FIX(ctout.WriteTotalTimeoutMultiplier); +} + +static void delay_ms(time) + int time; +{ + HANDLE ev; + + ev = CreateEvent(NULL, FALSE, FALSE, NULL); + if (!ev) + rb_sys_fail("CreateEvent"); + if (WaitForSingleObject(ev, time) == WAIT_FAILED) + rb_sys_fail("WaitForSingleObject"); + CloseHandle(ev); +} + +static VALUE sp_break(self, time) + VALUE self, time; +{ + HANDLE fh; + + Check_Type(time, T_FIXNUM); + + fh = sp_get_handle(self); + if (SetCommBreak(fh) == 0) + rb_sys_fail("SetCommBreak"); + delay_ms(FIX2INT(time) * 100); + ClearCommBreak(fh); + return Qnil; +} + +static void get_line_signals(obj, ls) + VALUE obj; + struct line_signals *ls; +{ + HANDLE fh; + int status; + + fh = sp_get_handle(obj); + if (GetCommModemStatus(fh, &status) == 0) + rb_sys_fail("GetCommModemStatus"); + + ls->cts = (status & MS_CTS_ON ? 1 : 0); + ls->dsr = (status & MS_DSR_ON ? 1 : 0); + ls->dcd = (status & MS_RLSD_ON ? 1 : 0); + ls->ri = (status & MS_RING_ON ? 1 : 0); +} + +static VALUE set_signal(obj, val, sigoff, sigon) + VALUE obj,val; + int sigoff, sigon; +{ + HANDLE fh; + int set, sig; + + Check_Type(val, T_FIXNUM); + fh = sp_get_handle(obj); + + set = FIX2INT(val); + if (set == 0) + sig = sigoff; + else if (set == 1) + sig = sigon; + else + rb_raise(rb_eArgError, "invalid value"); + + if (EscapeCommFunction(fh, sig) == 0) + rb_sys_fail("EscapeCommFunction"); + return obj; +} + +static VALUE sp_set_rts(self, val) + VALUE self, val; +{ + return set_signal(self, val, CLRRTS, SETRTS); +} + +static VALUE sp_set_dtr(self, val) + VALUE self, val; +{ + return set_signal(self, val, CLRDTR, SETDTR); +} + +static VALUE sp_get_rts(self) + VALUE self; +{ + rb_notimplement(); + return self; +} + +static VALUE sp_get_dtr(self) + VALUE self; +{ + rb_notimplement(); + return self; +} + + +#else /* defined(mswin) || defined(bccwin) */ + + +#include /* Standard input/output definitions */ +#include /* UNIX standard function definitions */ +#include /* File control definitions */ +#include /* Error number definitions */ +#include /* POSIX terminal control definitions */ +#include + +#ifdef CRTSCTS +#define HAVE_FLOWCONTROL_HARD 1 +#else +#undef HAVE_FLOWCONTROL_HARD +#endif + +#define NONE 0 +#define HARD 1 +#define SOFT 2 + +#define SPACE 0 +#define MARK 0 +#define EVEN 1 +#define ODD 2 + +#define PROCESSED 1 +#define RAW 2 + +static char sTcgetattr[] = "tcgetattr"; +static char sTcsetattr[] = "tcsetattr"; +static char sIoctl[] = "ioctl"; +static char sFcntl[] = "fcntl"; + + +static int sp_get_fd(obj) + VALUE obj; +{ + OpenFile *fptr; + + GetOpenFile(obj, fptr); + return (fileno(fptr->f)); +} + +static VALUE sp_create(class, _port) + VALUE class, _port; +{ + OpenFile *fp; + int fd; + int num_port; + char *port; + char *ports[] = { +#if defined(linux) || defined(cygwin) + "/dev/ttyS0", "/dev/ttyS1", "/dev/ttyS2", "/dev/ttyS3", + "/dev/ttyS4", "/dev/ttyS5", "/dev/ttyS6", "/dev/ttyS7" +#elif defined(freebsd) || defined(netbsd) || defined(openbsd) + "/dev/cuaa0", "/dev/cuaa1", "/dev/cuaa2", "/dev/cuaa3", + "/dev/cuaa4", "/dev/cuaa5", "/dev/cuaa6", "/dev/cuaa7" +#elif defined(solaris) + "/dev/ttya", "/dev/ttyb", "/dev/ttyc", "/dev/ttyd", + "/dev/ttye", "/dev/ttyf", "/dev/ttyg", "/dev/ttyh" +#elif defined(aix) + "/dev/tty0", "/dev/tty1", "/dev/tty2", "/dev/tty3", + "/dev/tty4", "/dev/tty5", "/dev/tty6", "/dev/tty7" +#elif defined(irix) + "/dev/ttyf1", "/dev/ttyf2", "/dev/ttyf3", "/dev/ttyf4", + "/dev/ttyf5", "/dev/ttyf6", "/dev/ttyf7", "/dev/ttyf8" +#endif + }; + struct termios params; + + NEWOBJ(sp, struct RFile); + rb_secure(4); + OBJSETUP(sp, class, T_FILE); + MakeOpenFile((VALUE)sp, fp); + + switch(TYPE(_port)) { + case T_FIXNUM: + num_port = FIX2INT(_port); + if (num_port < 0 || num_port > sizeof(ports) / sizeof(ports[0])) + rb_raise(rb_eArgError, "illegal port number"); + port = ports[num_port]; + break; + + case T_STRING: + Check_SafeStr(_port); + port = RSTRING(_port)->ptr; + break; + + default: + rb_raise(rb_eTypeError, "wrong argument type"); + break; + } + + fd = open(port, O_RDWR | O_NOCTTY | O_NDELAY); + if (fd == -1) + rb_sys_fail(port); + if (!isatty(fd)) { + close(fd); + rb_raise(rb_eArgError, "not a serial port"); + } + + if (tcgetattr(fd, ¶ms) == -1) { + close(fd); + rb_sys_fail(sTcgetattr); + } + params.c_oflag = 0; + params.c_lflag = 0; + params.c_iflag &= (IXON | IXOFF | IXANY); + params.c_cflag |= CLOCAL | CREAD; + params.c_cflag &= ~HUPCL; + if (tcsetattr(fd, TCSANOW, ¶ms) == -1) { + close(fd); + rb_sys_fail(sTcsetattr); + } + + fp->f = rb_fdopen(fd, "r+"); + fp->mode = FMODE_READWRITE | FMODE_SYNC; + return (VALUE) sp; +} + +static VALUE sp_set_modem_params(argc, argv, self) + int argc; + VALUE *argv, self; +{ + int fd; + struct termios params; + VALUE _data_rate = 0, _data_bits = 0, _parity = NONE, _stop_bits = 0; + int use_hash = 0; + int data_rate, data_bits; + + if (argc == 0) + return self; + if (argc == 1 && T_HASH == TYPE(argv[0])) { + use_hash = 1; + _data_rate = rb_hash_aref(argv[0], sBaud); + _data_bits = rb_hash_aref(argv[0], sDataBits); + _stop_bits = rb_hash_aref(argv[0], sStopBits); + _parity = rb_hash_aref(argv[0], sParity); + } + + fd = sp_get_fd(self); + if (tcgetattr(fd, ¶ms) == -1) + rb_sys_fail(sTcgetattr); + + if (!use_hash) + _data_rate = argv[0]; + if (NIL_P(_data_rate)) + goto SkipDataRate; + Check_Type(_data_rate, T_FIXNUM); + + switch(FIX2INT(_data_rate)) { + case 50: data_rate = B50; break; + case 75: data_rate = B75; break; + case 110: data_rate = B110; break; + case 134: data_rate = B134; break; + case 150: data_rate = B150; break; + case 200: data_rate = B200; break; + case 300: data_rate = B300; break; + case 600: data_rate = B600; break; + case 1200: data_rate = B1200; break; + case 1800: data_rate = B1800; break; + case 2400: data_rate = B2400; break; + case 4800: data_rate = B4800; break; + case 9600: data_rate = B9600; break; + case 19200: data_rate = B19200; break; + case 38400: data_rate = B38400; break; +#ifdef B57600 + case 57600: data_rate = B57600; break; +#endif +#ifdef B76800 + case 76800: data_rate = B76800; break; +#endif +#ifdef B115200 + case 115200: data_rate = B115200; break; +#endif +#ifdef B230400 + case 230400: data_rate = B230400; break; +#endif + + default: + rb_raise(rb_eArgError, "unknown baud rate"); + break; + } + cfsetispeed(¶ms, data_rate); + cfsetospeed(¶ms, data_rate); +SkipDataRate: + + if (!use_hash) + _data_bits = (argc >= 2 ? argv[1] : INT2FIX(8)); + if (NIL_P(_data_bits)) + goto SkipDataBits; + Check_Type(_data_bits, T_FIXNUM); + + switch(FIX2INT(_data_bits)) { + case 5: + data_bits = CS5; + break; + case 6: + data_bits = CS6; + break; + case 7: + data_bits = CS7; + break; + case 8: + data_bits = CS8; + break; + default: + rb_raise(rb_eArgError, "unknown character size"); + break; + } + params.c_cflag &= ~CSIZE; + params.c_cflag |= data_bits; +SkipDataBits: + + if (!use_hash) + _stop_bits = (argc >= 3 ? argv[2] : INT2FIX(1)); + if (NIL_P(_stop_bits)) + goto SkipStopBits; + Check_Type(_stop_bits, T_FIXNUM); + + switch(FIX2INT(_stop_bits)) { + case 1: + params.c_cflag &= ~CSTOPB; + break; + case 2: + params.c_cflag |= CSTOPB; + break; + default: + rb_raise(rb_eArgError, "unknown number of stop bits"); + break; + } +SkipStopBits: + + if (!use_hash) + _parity = (argc >= 4 ? argv[3] : ((params.c_cflag & CSIZE) == CS8 ? + INT2FIX(NONE) : INT2FIX(EVEN))); + if (NIL_P(_parity)) + goto SkipParity; + Check_Type(_parity, T_FIXNUM); + + switch(FIX2INT(_parity)) { + case EVEN: + params.c_cflag |= PARENB; + params.c_cflag &= ~PARODD; + break; + + case ODD: + params.c_cflag |= PARENB; + params.c_cflag |= PARODD; + break; + + case NONE: + params.c_cflag &= ~PARENB; + break; + + default: + rb_raise(rb_eArgError, "unknown parity"); + break; + } +SkipParity: + + if (tcsetattr(fd, TCSANOW, ¶ms) == -1) + rb_sys_fail(sTcsetattr); + return self; +} + +static void get_modem_params(self, mp) + VALUE self; + struct modem_params *mp; +{ + int fd; + struct termios params; + + fd = sp_get_fd(self); + if (tcgetattr(fd, ¶ms) == -1) + rb_sys_fail(sTcgetattr); + + switch (cfgetospeed(¶ms)) { + case B50: mp->data_rate = 50; break; + case B75: mp->data_rate = 75; break; + case B110: mp->data_rate = 110; break; + case B134: mp->data_rate = 134; break; + case B150: mp->data_rate = 150; break; + case B200: mp->data_rate = 200; break; + case B300: mp->data_rate = 300; break; + case B600: mp->data_rate = 600; break; + case B1200: mp->data_rate = 1200; break; + case B1800: mp->data_rate = 1800; break; + case B2400: mp->data_rate = 2400; break; + case B4800: mp->data_rate = 4800; break; + case B9600: mp->data_rate = 9600; break; + case B19200: mp->data_rate = 19200; break; + case B38400: mp->data_rate = 38400; break; +#ifdef B57600 + case B57600: mp->data_rate = 57600; break; +#endif +#ifdef B76800 + case B76800: mp->data_rate = 76800; break; +#endif +#ifdef B115200 + case B115200: mp->data_rate = 115200; break; +#endif +#ifdef B230400 + case B230400: mp->data_rate = 230400; break; +#endif + } + + switch(params.c_cflag & CSIZE) { + case CS5: + mp->data_bits = 5; + break; + case CS6: + mp->data_bits = 6; + break; + case CS7: + mp->data_bits = 7; + break; + case CS8: + mp->data_bits = 8; + break; + default: + mp->data_bits = 0; + break; + } + + mp->stop_bits = (params.c_cflag & CSTOPB ? 2 : 1); + + if (!(params.c_cflag & PARENB)) + mp->parity = NONE; + else if (params.c_cflag & PARODD) + mp->parity = ODD; + else + mp->parity = EVEN; +} + +static VALUE sp_set_flow_control(self, val) + VALUE self, val; +{ + int fd; + int flowc; + struct termios params; + + Check_Type(val, T_FIXNUM); + + fd = sp_get_fd(self); + if (tcgetattr(fd, ¶ms) == -1) + rb_sys_fail(sTcgetattr); + + flowc = FIX2INT(val); + if (flowc & HARD) +#ifdef HAVE_FLOWCONTROL_HARD + params.c_cflag |= CRTSCTS; + else + params.c_cflag &= ~CRTSCTS; +#else + rb_raise(rb_eIOError, "Hardware flow control not supported"); +#endif + if (flowc & SOFT) + params.c_iflag |= (IXON | IXOFF | IXANY); + else + params.c_iflag &= ~(IXON | IXOFF | IXANY); + + if (tcsetattr(fd, TCSANOW, ¶ms) == -1) + rb_sys_fail(sTcsetattr); + return self; +} + +static VALUE sp_get_flow_control(self) + VALUE self; +{ + int ret; + int fd; + struct termios params; + + fd = sp_get_fd(self); + if (tcgetattr(fd, ¶ms) == -1) + rb_sys_fail(sTcgetattr); + + ret = 0; +#ifdef HAVE_FLOWCONTROL_HARD + if (params.c_cflag & CRTSCTS) + ret += HARD; +#endif + if (params.c_iflag & (IXON | IXOFF | IXANY)) + ret += SOFT; + + return INT2FIX(ret); +} + +static VALUE sp_set_input_type(self, val) + VALUE self, val; +{ + int fd; + int type; + struct termios params; + + Check_Type(val, T_FIXNUM); + + fd = sp_get_fd(self); + if (tcgetattr(fd, ¶ms) == -1) + rb_sys_fail(sTcgetattr); + + type = FIX2INT(val); + if (type == PROCESSED) + params.c_lflag |= ICANON; + else + params.c_lflag &= ~(ICANON | ECHO | ECHOE | ISIG); + + if (tcsetattr(fd, TCSANOW, ¶ms) == -1) + rb_sys_fail(sTcsetattr); + + return self; +} + +static VALUE sp_get_input_type(self) + VALUE self; +{ + int ret; + int fd; + struct termios params; + + fd = sp_get_fd(self); + if (tcgetattr(fd, ¶ms) == -1) + rb_sys_fail(sTcgetattr); + + ret = 0; + if (params.c_lflag & ICANON) + ret = PROCESSED; + else + ret = RAW; + + return INT2FIX(ret); +} + +static VALUE sp_set_output_type(self, val) + VALUE self, val; +{ + int fd; + int type; + struct termios params; + + Check_Type(val, T_FIXNUM); + + fd = sp_get_fd(self); + if (tcgetattr(fd, ¶ms) == -1) + rb_sys_fail(sTcgetattr); + + type = FIX2INT(val); + if (type == PROCESSED) + params.c_oflag |= OPOST; + else + params.c_oflag &= ~OPOST; + + if (tcsetattr(fd, TCSANOW, ¶ms) == -1) + rb_sys_fail(sTcsetattr); + + return self; +} + +static VALUE sp_get_output_type(self) + VALUE self; +{ + int ret; + int fd; + struct termios params; + + fd = sp_get_fd(self); + if (tcgetattr(fd, ¶ms) == -1) + rb_sys_fail(sTcgetattr); + + ret = 0; + if (params.c_oflag & OPOST) + ret = PROCESSED; + else + ret = RAW; + + return INT2FIX(ret); +} + +static VALUE sp_set_nonblock(self, val) + VALUE self, val; +{ + int fd; + int flags; + + fd = sp_get_fd(self); + + flags = fcntl(fd, F_GETFL, 0); + if(flags == -1) + rb_sys_fail(sFcntl); + + if (val == Qtrue) { + if(fcntl(fd, F_SETFL, flags | O_NONBLOCK) == -1) + rb_sys_fail(sFcntl); + } + else if (val == Qfalse) { + if(fcntl(fd, F_SETFL, flags & ~O_NONBLOCK) == -1) + rb_sys_fail(sFcntl); + } + else + rb_raise(rb_eArgError, "invalid value"); + + return self; +} + +static VALUE sp_get_nonblock(self) + VALUE self; +{ + int fd; + int flags; + + fd = sp_get_fd(self); + flags = fcntl(fd, F_GETFL, 0); + if(flags == -1) + rb_sys_fail(sFcntl); + + return (flags & O_NONBLOCK) ? Qtrue : Qfalse; +} + +static VALUE sp_set_read_timeout(self, val) + VALUE self, val; +{ + int timeout; + int fd; + struct termios params; + + Check_Type(val, T_FIXNUM); + timeout = FIX2INT(val); + + fd = sp_get_fd(self); + if (tcgetattr(fd, ¶ms) == -1) + rb_sys_fail(sTcgetattr); + + if (timeout < 0) { + params.c_cc[VTIME] = 0; + params.c_cc[VMIN] = 0; + } else if (timeout == 0) { + params.c_cc[VTIME] = 0; + params.c_cc[VMIN] = 1; + } else { + params.c_cc[VTIME] = (timeout + 50) / 100; + params.c_cc[VMIN] = 0; + } + + if (tcsetattr(fd, TCSANOW, ¶ms) == -1) + rb_sys_fail(sTcsetattr); + return self; +} + +static VALUE sp_get_read_timeout(self) + VALUE self; +{ + int fd; + struct termios params; + + fd = sp_get_fd(self); + if (tcgetattr(fd, ¶ms) == -1) + rb_sys_fail(sTcgetattr); + if (params.c_cc[VTIME] == 0 && params.c_cc[VMIN] == 0) + return INT2FIX(-1); + return INT2FIX(params.c_cc[VTIME] * 100); +} + +static VALUE sp_set_write_timeout(self, val) + VALUE self, val; +{ + rb_notimplement(); + return self; +} + +static VALUE sp_get_write_timeout(self) + VALUE self; +{ + rb_notimplement(); + return self; +} + +static VALUE sp_break(self, time) + VALUE self, time; +{ + int fd; + + Check_Type(time, T_FIXNUM); + + fd = sp_get_fd(self); + if (tcsendbreak(fd, FIX2INT(time) / 3) == -1) + rb_sys_fail("tcsendbreak"); + return Qnil; +} + +static void get_line_signals(obj, ls) + VALUE obj; + struct line_signals *ls; +{ + int fd, status; + + fd = sp_get_fd(obj); + if (ioctl(fd, TIOCMGET, &status) == -1) + rb_sys_fail(sIoctl); + + ls->rts = (status & TIOCM_RTS ? 1 : 0); + ls->dtr = (status & TIOCM_DTR ? 1 : 0); + ls->cts = (status & TIOCM_CTS ? 1 : 0); + ls->dsr = (status & TIOCM_DSR ? 1 : 0); + ls->dcd = (status & TIOCM_CD ? 1 : 0); + ls->ri = (status & TIOCM_RI ? 1 : 0); +} + +static VALUE set_signal(obj, val, sig) + VALUE obj,val; + int sig; +{ + int status; + int fd; + int set; + + Check_Type(val, T_FIXNUM); + fd = sp_get_fd(obj); + if (ioctl(fd, TIOCMGET, &status) == -1) + rb_sys_fail(sIoctl); + + set = FIX2INT(val); + if (set == 0) + status &= ~sig; + else if (set == 1) + status |= sig; + else + rb_raise(rb_eArgError, "invalid value"); + + if (ioctl(fd, TIOCMSET, &status) == -1) + rb_sys_fail(sIoctl); + return obj; +} + +static VALUE sp_set_rts(self, val) + VALUE self, val; +{ + return set_signal(self, val, TIOCM_RTS); +} + +static VALUE sp_set_dtr(self, val) + VALUE self, val; +{ + return set_signal(self, val, TIOCM_DTR); +} + +static VALUE sp_get_rts(self) + VALUE self; +{ + struct line_signals ls; + + get_line_signals(self, &ls); + return INT2FIX(ls.rts); +} + +static VALUE sp_get_dtr(self) + VALUE self; +{ + struct line_signals ls; + + get_line_signals(self, &ls); + return INT2FIX(ls.dtr); +} + + +#endif /* defined(mswin) || defined(bccwin) */ + + +static VALUE sp_set_data_rate(self, data_rate) + VALUE self, data_rate; +{ + VALUE argv[4]; + + argv[0] = data_rate; + argv[1] = argv[2] = argv[3] = Qnil; + return sp_set_modem_params(4, argv, self); +} + +static VALUE sp_set_data_bits(self, data_bits) + VALUE self, data_bits; +{ + VALUE argv[4]; + + argv[1] = data_bits; + argv[0] = argv[2] = argv[3] = Qnil; + return sp_set_modem_params(4, argv, self); +} + +static VALUE sp_set_stop_bits(self, stop_bits) + VALUE self, stop_bits; +{ + VALUE argv[4]; + + argv[2] = stop_bits; + argv[0] = argv[1] = argv[3] = Qnil; + return sp_set_modem_params(4, argv, self); +} + +static VALUE sp_set_parity(self, parity) + VALUE self, parity; +{ + VALUE argv[4]; + + argv[3] = parity; + argv[0] = argv[1] = argv[2] = Qnil; + return sp_set_modem_params(4, argv, self); +} + +static VALUE sp_get_data_rate(self) + VALUE self; +{ + struct modem_params mp; + + get_modem_params(self, &mp); + return INT2FIX(mp.data_rate); +} + +static VALUE sp_get_data_bits(self) + VALUE self; +{ + struct modem_params mp; + + get_modem_params(self, &mp); + return INT2FIX(mp.data_bits); +} + +static VALUE sp_get_stop_bits(self) + VALUE self; +{ + struct modem_params mp; + + get_modem_params(self, &mp); + return INT2FIX(mp.stop_bits); +} + +static VALUE sp_get_parity(self) + VALUE self; +{ + struct modem_params mp; + + get_modem_params(self, &mp); + return INT2FIX(mp.parity); +} + +static VALUE sp_get_modem_params(self) + VALUE self; +{ + struct modem_params mp; + VALUE hash; + + get_modem_params(self, &mp); + hash = rb_hash_new(); + rb_hash_aset(hash, sBaud, INT2FIX(mp.data_rate)); + rb_hash_aset(hash, sDataBits, INT2FIX(mp.data_bits)); + rb_hash_aset(hash, sStopBits, INT2FIX(mp.stop_bits)); + rb_hash_aset(hash, sParity, INT2FIX(mp.parity)); + return hash; +} + +static VALUE sp_get_cts(self) + VALUE self; +{ + struct line_signals ls; + + get_line_signals(self, &ls); + return INT2FIX(ls.cts); +} + +static VALUE sp_get_dsr(self) + VALUE self; +{ + struct line_signals ls; + + get_line_signals(self, &ls); + return INT2FIX(ls.dsr); +} + +static VALUE sp_get_dcd(self) + VALUE self; +{ + struct line_signals ls; + + get_line_signals(self, &ls); + return INT2FIX(ls.dcd); +} + +static VALUE sp_get_ri(self) + VALUE self; +{ + struct line_signals ls; + + get_line_signals(self, &ls); + return INT2FIX(ls.ri); +} + +static VALUE +sp_signals(self) + VALUE self; +{ + struct line_signals ls; + VALUE hash; + + get_line_signals(self, &ls); + hash = rb_hash_new(); +#if !(defined(mswin) || defined(bccwin)) + rb_hash_aset(hash, sRts, INT2FIX(ls.rts)); + rb_hash_aset(hash, sDtr, INT2FIX(ls.dtr)); +#endif + rb_hash_aset(hash, sCts, INT2FIX(ls.cts)); + rb_hash_aset(hash, sDsr, INT2FIX(ls.dsr)); + rb_hash_aset(hash, sDcd, INT2FIX(ls.dcd)); + rb_hash_aset(hash, sRi, INT2FIX(ls.ri)); + return hash; +} + +void Init_serialport() { + sBaud = rb_str_new2("baud"); + sDataBits = rb_str_new2("data_bits"); + sStopBits = rb_str_new2("stop_bits"); + sParity = rb_str_new2("parity"); + sRts = rb_str_new2("rts"); + sDtr = rb_str_new2("dtr"); + sCts = rb_str_new2("cts"); + sDsr = rb_str_new2("dsr"); + sDcd = rb_str_new2("dcd"); + sRi = rb_str_new2("ri"); + + rb_gc_register_address(&sBaud); + rb_gc_register_address(&sDataBits); + rb_gc_register_address(&sStopBits); + rb_gc_register_address(&sParity); + rb_gc_register_address(&sRts); + rb_gc_register_address(&sDtr); + rb_gc_register_address(&sCts); + rb_gc_register_address(&sDsr); + rb_gc_register_address(&sDcd); + rb_gc_register_address(&sRi); + + cSerialPort = rb_define_class("SerialPort", rb_cIO); + rb_define_singleton_method(cSerialPort, "create", sp_create, 1); + + rb_define_method(cSerialPort, "get_modem_params", sp_get_modem_params, 0); + rb_define_method(cSerialPort, "set_modem_params", sp_set_modem_params, -1); + rb_define_method(cSerialPort, "modem_params", sp_get_modem_params, 0); + rb_define_method(cSerialPort, "modem_params=", sp_set_modem_params, -1); + rb_define_method(cSerialPort, "baud", sp_get_data_rate, 0); + rb_define_method(cSerialPort, "baud=", sp_set_data_rate, 1); + rb_define_method(cSerialPort, "data_bits", sp_get_data_bits, 0); + rb_define_method(cSerialPort, "data_bits=", sp_set_data_bits, 1); + rb_define_method(cSerialPort, "stop_bits", sp_get_stop_bits, 0); + rb_define_method(cSerialPort, "stop_bits=", sp_set_stop_bits, 1); + rb_define_method(cSerialPort, "parity", sp_get_parity, 0); + rb_define_method(cSerialPort, "parity=", sp_set_parity, 1); + + rb_define_method(cSerialPort, "flow_control=", sp_set_flow_control, 1); + rb_define_method(cSerialPort, "flow_control", sp_get_flow_control, 0); + rb_define_method(cSerialPort, "input_type=", sp_set_input_type, 1); + rb_define_method(cSerialPort, "input_type", sp_get_input_type, 0); + rb_define_method(cSerialPort, "output_type=", sp_set_output_type, 1); + rb_define_method(cSerialPort, "output_type", sp_get_output_type, 0); + + rb_define_method(cSerialPort, "nonblock=", sp_set_nonblock, 1); + rb_define_method(cSerialPort, "nonblock", sp_get_nonblock, 0); + + rb_define_method(cSerialPort, "read_timeout", sp_get_read_timeout, 0); + rb_define_method(cSerialPort, "read_timeout=", sp_set_read_timeout, 1); + rb_define_method(cSerialPort, "write_timeout", sp_get_write_timeout, 0); + rb_define_method(cSerialPort, "write_timeout=", sp_set_write_timeout, 1); + + rb_define_method(cSerialPort, "break", sp_break, 1); + + rb_define_method(cSerialPort, "signals", sp_signals, 0); + rb_define_method(cSerialPort, "get_signals", sp_signals, 0); + rb_define_method(cSerialPort, "rts", sp_get_rts, 0); + rb_define_method(cSerialPort, "rts=", sp_set_rts, 1); + rb_define_method(cSerialPort, "dtr", sp_get_dtr, 0); + rb_define_method(cSerialPort, "dtr=", sp_set_dtr, 1); + rb_define_method(cSerialPort, "cts", sp_get_cts, 0); + rb_define_method(cSerialPort, "dsr", sp_get_dsr, 0); + rb_define_method(cSerialPort, "dcd", sp_get_dcd, 0); + rb_define_method(cSerialPort, "ri", sp_get_ri, 0); + + rb_define_const(cSerialPort, "NONE", INT2FIX(NONE)); + rb_define_const(cSerialPort, "HARD", INT2FIX(HARD)); + rb_define_const(cSerialPort, "SOFT", INT2FIX(SOFT)); + + rb_define_const(cSerialPort, "SPACE", INT2FIX(SPACE)); + rb_define_const(cSerialPort, "MARK", INT2FIX(MARK)); + rb_define_const(cSerialPort, "EVEN", INT2FIX(EVEN)); + rb_define_const(cSerialPort, "ODD", INT2FIX(ODD)); + + rb_define_const(cSerialPort, "PROCESSED", INT2FIX(PROCESSED)); + rb_define_const(cSerialPort, "RAW", INT2FIX(RAW)); + + rb_define_const(cSerialPort, "VERSION", rb_str_new2(VERSION)); + + /* The following definitions are more easily carried out in Ruby */ + rb_eval_string( + "class SerialPort\n" + + "def self.new(port, *params)\n" + "sp = create(port)\n" + "begin\n" + "sp.set_modem_params(*params)\n" + "rescue\n" + "sp.close\n" + "raise\n" + "end\n" + "return sp\n" + "end\n" + + "def self.open(port, *params)\n" + "sp = create(port)\n" + "begin\n" + "sp.set_modem_params(*params)\n" + "if (block_given?)\n" + "yield sp\n" + "sp.close\n" + "return nil\n" + "end\n" + "rescue\n" + "sp.close\n" + "raise\n" + "end\n" + "return sp\n" + "end\n" + + "end\n" + ); +} diff --git a/external/serialport/test/miniterm.rb b/external/serialport/test/miniterm.rb new file mode 100644 index 0000000000..2a876cac20 --- /dev/null +++ b/external/serialport/test/miniterm.rb @@ -0,0 +1,25 @@ +require "../serialport.so" + + +if ARGV.size < 4 + STDERR.print <