├── Gemfile ├── lib ├── serialport │ └── version.rb └── serialport.rb ├── test ├── set_readtimeout.rb └── miniterm.rb ├── .gitignore ├── CHECKLIST ├── ext └── native │ ├── extconf.rb │ ├── serialport.h │ ├── win_serialport_impl.c │ ├── serialport.c │ └── posix_serialport_impl.c ├── Rakefile ├── serialport.gemspec ├── README.md ├── .github └── workflows │ └── ci.yml ├── CHANGELOG └── LICENSE /Gemfile: -------------------------------------------------------------------------------- 1 | source "https://rubygems.org" 2 | 3 | gemspec 4 | -------------------------------------------------------------------------------- /lib/serialport/version.rb: -------------------------------------------------------------------------------- 1 | class SerialPort < IO 2 | VERSION = "1.4.0" 3 | end 4 | -------------------------------------------------------------------------------- /test/set_readtimeout.rb: -------------------------------------------------------------------------------- 1 | #!/usr/bin/ruby 2 | require "rubygems" 3 | require "serialport" 4 | 5 | sp = SerialPort.new("/dev/tty.usbserial", "9600".to_i) 6 | sp.read_timeout = 100 7 | sp.close 8 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | /pkg 3 | /doc 4 | /tmp 5 | /ext/native/*.bundle 6 | /ext/native/*.log 7 | /ext/native/*.o 8 | /ext/native/*.so 9 | /ext/native/Makefile 10 | lib/*.so 11 | .*.swp 12 | tags 13 | *.gem 14 | *.bundle 15 | Gemfile.lock 16 | .yardoc 17 | -------------------------------------------------------------------------------- /CHECKLIST: -------------------------------------------------------------------------------- 1 | # Ruby-Serialport Gem Release Checklist 2 | 3 | * Update documentation 4 | * Update lib/serialport/version.rb 5 | * Update CHANGELOG 6 | * Update README 7 | * `rake build` 8 | * `rake install` 9 | * Test build 10 | * `git tag -a v{VERSION} -m "v{VERSION}: {ONE-LINE DESCRIPTION}"` 11 | * Merge and Push 12 | * `rake release` -------------------------------------------------------------------------------- /ext/native/extconf.rb: -------------------------------------------------------------------------------- 1 | require 'mkmf' 2 | 3 | printf("checking for OS... ") 4 | STDOUT.flush 5 | os = /-([a-z]+)/.match(RUBY_PLATFORM)[1] 6 | puts(os) 7 | $CFLAGS += " -DOS_#{os.upcase}" 8 | 9 | if !(os == 'mswin' or os == 'bccwin' or os == 'mingw') 10 | exit(1) if not have_header("termios.h") or not have_header("unistd.h") 11 | end 12 | 13 | have_func("rb_io_descriptor") # ruby-3.1+ 14 | have_func("rb_io_open_descriptor") # ruby-3.3+ 15 | 16 | create_makefile('serialport') 17 | -------------------------------------------------------------------------------- /test/miniterm.rb: -------------------------------------------------------------------------------- 1 | require "../serialport.so" 2 | 3 | 4 | if ARGV.size < 4 5 | STDERR.print < [:clean, :clobber, :compile, :test] -------------------------------------------------------------------------------- /serialport.gemspec: -------------------------------------------------------------------------------- 1 | # -*- encoding: utf-8 -*- 2 | $: << "lib" 3 | require "serialport/version" 4 | 5 | Gem::Specification.new do |s| 6 | s.name = "serialport" 7 | s.license = "GPL-2" 8 | s.version = SerialPort::VERSION 9 | s.authors = ["Guillaume Pierronnet", "Alan Stern", "Daniel E. Shipton", "Tobin Richard", "Hector Parra", "Ryan C. Payne", "Lars Kanis"] 10 | s.summary = "Library for using RS-232 serial ports." 11 | s.description = "Ruby/SerialPort is a Ruby library that provides a class for using RS-232 serial ports." 12 | s.email = "lars@greiz-reinsdorf.de" 13 | s.homepage = "http://github.com/larskanis/ruby-serialport/" 14 | 15 | s.required_ruby_version = '>= 2.5' 16 | s.add_development_dependency "bundler" 17 | s.add_development_dependency "rake" 18 | s.add_development_dependency "rake-compiler", ">= 0.4.1" 19 | 20 | s.require_paths = ["lib"] 21 | s.files = `git ls-files`.split($\) 22 | s.extensions = "ext/native/extconf.rb" 23 | s.executables = s.files.grep(%r{^bin/}).map{ |f| File.basename(f) } 24 | s.extra_rdoc_files = ["LICENSE", "README.md"] 25 | end 26 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Ruby/SerialPort 2 | 3 | [![CI](https://github.com/larskanis/ruby-serialport/actions/workflows/ci.yml/badge.svg)](https://github.com/larskanis/ruby-serialport/actions/workflows/ci.yml) 4 | 5 | ## Description 6 | 7 | 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. 8 | 9 | There is an alternative gem with MRI, JRuby, and Rubinius support. See below. 10 | 11 | ## Installation 12 | 13 | Install the gem as normal: `sudo gem install serialport` 14 | 15 | ## Testing 16 | 17 | Use Ruby's version of miniterm: `ruby test/miniterm.rb` 18 | 19 | ## API 20 | 21 | See or run `yard` 22 | 23 | ## Issues 24 | 25 | See 26 | 27 | ## Alternatives 28 | 29 | The [Hybrid Group](http://hybridgroup.com/)'s MRI, JRuby, & Rubinius compatible [rubyserial](https://github.com/hybridgroup/rubyserial) gem may provide improved compatibilty in modern environments. 30 | 31 | ## License 32 | 33 | GPL 34 | 35 | ## Authors 36 | 37 | * Guillaume Pierronnet 38 | * Alan Stern 39 | * Tobin Richard 40 | * Hector Parra 41 | * Ryan C. Payne 42 | * Lars Kanis 43 | -------------------------------------------------------------------------------- /lib/serialport.rb: -------------------------------------------------------------------------------- 1 | require 'serialport.so' 2 | 3 | 4 | # This class is used for communication over a serial port. 5 | # In addition to the methods here, you can use Ruby IO methods, e.g. read, write, getc, readlines, etc. 6 | # 7 | # @see http://rubydoc.info/stdlib/core/IO Ruby IO class 8 | # @see http://www.cmrr.umn.edu/~strupp/serial.html "Serial Programming Guide for POSIX Operating Systems" 9 | class SerialPort < IO 10 | autoload :VERSION, 'serialport/version' 11 | 12 | private_class_method(:create) 13 | 14 | # Creates a serial port object. 15 | # Accepts the port identifier and a variable list for configuration as paramaters or hash. 16 | # Please see SerialPort#set_modem_params 17 | # 18 | # @overload new(port, *params) 19 | # @param port [Integer] the serial port number, 20 | # where 0 is mapped to "COM1" on Windows, "/dev/ttyS0" on Linux, "/dev/cuaa0" on Mac OS X, etc. 21 | # @overload new(port, *params) 22 | # @param port [String] the serial port file e.g. "/dev/ttyS0" 23 | # @return [SerialPort] 24 | # @see SerialPort#set_modem_params 25 | def SerialPort::new(port, *params) 26 | sp = create(port) 27 | begin 28 | sp.set_modem_params(*params) 29 | rescue 30 | sp.close 31 | raise 32 | end 33 | return sp 34 | end 35 | 36 | # This behaves like SerialPort#new, except that you can pass a block 37 | # to which the new serial port object will be passed. In this case 38 | # the connection is automaticaly closed when the block has finished. 39 | # 40 | # @yield [serial_port] the serial port number or filename 41 | # @see SerialPort#new 42 | # @see SerialPort#set_modem_params 43 | def SerialPort::open(port, *params) 44 | sp = create(port) 45 | begin 46 | sp.set_modem_params(*params) 47 | rescue 48 | sp.close 49 | raise 50 | end 51 | if (block_given?) 52 | begin 53 | yield sp 54 | ensure 55 | sp.close 56 | end 57 | return nil 58 | end 59 | return sp 60 | end 61 | end 62 | -------------------------------------------------------------------------------- /.github/workflows/ci.yml: -------------------------------------------------------------------------------- 1 | name: CI 2 | on: 3 | workflow_dispatch: 4 | schedule: 5 | - cron: "0 5 * * 3" # At 05:00 on Wednesday # https://crontab.guru/#0_5_*_*_3 6 | push: 7 | branches: 8 | - master 9 | tags: 10 | - "*.*.*" 11 | pull_request: 12 | types: [opened, synchronize] 13 | branches: 14 | - "*" 15 | permissions: 16 | contents: read 17 | 18 | jobs: 19 | rcd_build: 20 | name: build gem 21 | 22 | runs-on: ubuntu-latest 23 | steps: 24 | - uses: actions/checkout@v4 25 | 26 | - uses: ruby/setup-ruby@v1 27 | with: 28 | ruby-version: "3.4" 29 | 30 | - name: Build ffi.gem 31 | run: | 32 | bundle install 33 | bundle exec rake gem 34 | 35 | - name: Upload binary gem 36 | uses: actions/upload-artifact@v4 37 | with: 38 | name: gem 39 | path: pkg/*-*.gem 40 | 41 | job_test_native: 42 | name: native test 43 | needs: rcd_build 44 | strategy: 45 | fail-fast: false 46 | matrix: 47 | os: 48 | - windows-latest 49 | - macos-13 50 | - macos-latest 51 | - ubuntu-latest 52 | - windows-11-arm 53 | ruby: 54 | - "head" 55 | - "3.4" 56 | - "3.3" 57 | - "3.2" 58 | - "3.1" 59 | - "3.0" 60 | - "2.7" 61 | - "2.6" 62 | - "2.5" 63 | exclude: 64 | - os: macos-latest 65 | ruby: "2.5" 66 | - os: windows-11-arm 67 | ruby: "3.3" 68 | - os: windows-11-arm 69 | ruby: "3.2" 70 | - os: windows-11-arm 71 | ruby: "3.1" 72 | - os: windows-11-arm 73 | ruby: "3.0" 74 | - os: windows-11-arm 75 | ruby: "2.7" 76 | - os: windows-11-arm 77 | ruby: "2.6" 78 | - os: windows-11-arm 79 | ruby: "2.5" 80 | 81 | runs-on: ${{ matrix.os }} 82 | steps: 83 | - uses: actions/checkout@v4 84 | - uses: ruby/setup-ruby@v1 85 | with: 86 | ruby-version: ${{ matrix.ruby }} 87 | - run: ruby --version 88 | - name: Download gem 89 | uses: actions/download-artifact@v4 90 | with: 91 | name: gem 92 | - name: Install gem 93 | run: gem install --local *.gem --verbose 94 | - name: Run tests 95 | run: | 96 | bundle install 97 | ruby -rserialport -S rake test 98 | -------------------------------------------------------------------------------- /ext/native/serialport.h: -------------------------------------------------------------------------------- 1 | /* Ruby/SerialPort 2 | * Guillaume Pierronnet 3 | * Alan Stern 4 | * Daniel E. Shipton 5 | * Tobin Richard 6 | * Ryan C. Payne 7 | * 8 | * This code is hereby licensed for public consumption under either the 9 | * GNU GPL v2 or greater. 10 | * 11 | * You should have received a copy of the GNU General Public License 12 | * along with this program; if not, write to the Free Software 13 | * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. 14 | * 15 | * For documentation on serial programming, see the excellent: 16 | * "Serial Programming Guide for POSIX Operating Systems" 17 | * written Michael R. Sweet. 18 | * http://www.easysw.com/~mike/serial/ 19 | */ 20 | 21 | #ifndef _RUBY_SERIAL_PORT_H_ 22 | #define _RUBY_SERIAL_PORT_H_ 23 | 24 | #include /* ruby inclusion */ 25 | #include 26 | 27 | #ifndef HAVE_RB_IO_OPEN_DESCRIPTOR 28 | VALUE io_open_descriptor_fallback(VALUE klass, int descriptor, int mode, VALUE path, VALUE timeout, void *encoding); 29 | #define rb_io_open_descriptor io_open_descriptor_fallback 30 | #endif 31 | 32 | struct modem_params 33 | { 34 | int data_rate; 35 | int data_bits; 36 | int stop_bits; 37 | int parity; 38 | }; 39 | 40 | struct line_signals 41 | { 42 | int rts; 43 | int dtr; 44 | int cts; 45 | int dsr; 46 | int dcd; 47 | int ri; 48 | }; 49 | 50 | #define NONE 0 51 | #define HARD 1 52 | #define SOFT 2 53 | 54 | #if defined(OS_MSWIN) || defined(OS_BCCWIN) || defined(OS_MINGW) 55 | #define SPACE SPACEPARITY 56 | #define MARK MARKPARITY 57 | #define EVEN EVENPARITY 58 | #define ODD ODDPARITY 59 | 60 | #define RB_SERIAL_EXPORT 61 | #else 62 | #define EVEN 1 63 | #define ODD 2 64 | #define SPACE 3 65 | #define MARK 4 66 | 67 | #define RB_SERIAL_EXPORT 68 | #endif 69 | 70 | extern VALUE sBaud, sDataBits, sStopBits, sParity; /* strings */ 71 | extern VALUE sRts, sDtr, sCts, sDsr, sDcd, sRi; 72 | 73 | /* Implementation specific functions. */ 74 | VALUE RB_SERIAL_EXPORT sp_create_impl(VALUE class, VALUE _port); 75 | VALUE RB_SERIAL_EXPORT sp_set_modem_params_impl(int argc, VALUE *argv, VALUE self); 76 | void RB_SERIAL_EXPORT get_modem_params_impl(VALUE self, struct modem_params *mp); 77 | VALUE RB_SERIAL_EXPORT sp_set_flow_control_impl(VALUE self, VALUE val); 78 | VALUE RB_SERIAL_EXPORT sp_get_flow_control_impl(VALUE self); 79 | VALUE RB_SERIAL_EXPORT sp_set_read_timeout_impl(VALUE self, VALUE val); 80 | VALUE RB_SERIAL_EXPORT sp_get_read_timeout_impl(VALUE self); 81 | VALUE RB_SERIAL_EXPORT sp_set_write_timeout_impl(VALUE self, VALUE val); 82 | VALUE RB_SERIAL_EXPORT sp_get_write_timeout_impl(VALUE self); 83 | VALUE RB_SERIAL_EXPORT sp_break_impl(VALUE self, VALUE time); 84 | void RB_SERIAL_EXPORT get_line_signals_helper_impl(VALUE obj, struct line_signals *ls); 85 | VALUE RB_SERIAL_EXPORT set_signal_impl(VALUE obj, VALUE val, int sig); 86 | VALUE RB_SERIAL_EXPORT sp_set_rts_impl(VALUE self, VALUE val); 87 | VALUE RB_SERIAL_EXPORT sp_set_dtr_impl(VALUE self, VALUE val); 88 | VALUE RB_SERIAL_EXPORT sp_get_rts_impl(VALUE self); 89 | VALUE RB_SERIAL_EXPORT sp_get_dtr_impl(VALUE self); 90 | 91 | VALUE RB_SERIAL_EXPORT sp_flush_input_data_impl(VALUE self); 92 | VALUE RB_SERIAL_EXPORT sp_flush_output_data_impl(VALUE self); 93 | 94 | #endif 95 | -------------------------------------------------------------------------------- /CHANGELOG: -------------------------------------------------------------------------------- 1 | 1.4.0 => 16-06-2025: [FIXED] Fix compatibility with ruby-3.4. [#79](https://github.com/hparra/ruby-serialport/pull/79) 2 | [FIXED] Replace ancient K&R function declarations by ANSI style. 3 | [FIXED] Fixed various compiler warnings. 4 | [FIXED] Avoid deprecated access to fptr->fd 5 | [CHANGED] New repository link and add Lars Kanis as new author. 6 | [CHANGED] Move CI from Travis to Github 7 | [CHANGED] Use autoload for SerialPort::VERSION 8 | [REMOVED] Remove compatibility to ruby < 2.5 9 | 10 | 1.3.2 => 29-09-2021: [FIXED] Remove calls to rb_secure because it is deprecated and removed in Ruby 3. 11 | 12 | 1.3.1 => 07/26/2014: [FIXED] Ruby 2.2 support 13 | [NEW] UNIX MARK/SPACE parity (CMSPAR) support 14 | 15 | 1.3.0 => 11/17/2013: [NEW] flush_input() and flush_output() methods [Manuel A. Güílamo (MaG)] 16 | 17 | 1.2.3 => 11/04/2013: [FIXED] Improved documentation (yard) 18 | 19 | 1.2.2 => 11/03/2013: [FIXED] warning: already initialized constant VERSION 20 | 21 | 1.2.1 => 10/25/2013: [FIXED] removed absurd circular dependency 22 | 23 | 1.2.0 => 10/25/2013: [NEW] rake-compiler, bundler, and travis ci integration 24 | 25 | 1.1.0 => 05/14/2012: [NEW] Ruby 2.0 Support [Aaron Patterson (tenderlove)] 26 | [FIXED] Support for baud rates up to 1000000 in Windows [Will Koehler (willkoehler)] 27 | 28 | 1.0.4 => 07/17/2010: [FIXED] [Windows] No longer restricted to specific bitrates 29 | [FIXED] [Windows] Removed potentially insecure sprintf() use 30 | [FIXED] [Windows] Workaround for rb_sys_fail not checking GetLastError() 31 | 32 | 1.0.3 => 04/08/2010: [FIXED] [Windows] Could not specify 10 or "COM10" or higher 33 | [FIXED] [Windows] Warning passing INT instead of LONG to GetCommModemStatus 34 | 35 | 1.0.2 => 04/06/2010: [FIXED] Passing a block into open did not properly handle a return from within the block 36 | 37 | 1.0.1 => 01/20/2010: [FIXED] Conditional RB_SERIAL_EXPORT needed for Visual Studio bonked GCC 38 | 39 | 1.0.0 => 01/12/2010: [FIXED] x86_64 segmentation faults 40 | [NEW] Windows Ruby 1.9 support 41 | 42 | 0.7.4 => 10/12/2009: [NEW] Conditional 1.8.6 & 1.9 support (POSIX only). 43 | [NEW] MinGW support. 44 | 45 | 0.7.3 => 10/09/2009: [NEW] POSIX Ruby 1.9 support 46 | 47 | 0.7.2 => 02/09/2008: Fix several Windows and POSIX compilation problems. 48 | Make GCC compile the code without any warnings. 49 | Make RDoc not be so talkative during gem installation. 50 | 51 | 0.7.1 => 31/08/2008: Change to gemspec creation and fix posix compilation. 52 | 53 | 0.7.0 => 03/07/2008: Major Code Cleanup 54 | 55 | 0.6.1 => 25/03/2003: Minor changes 56 | 57 | 0.6 => 12/02/2003: Windows support 58 | Get/set modem parameters 59 | Read and write timeouts 60 | Open method 61 | 62 | 0.5 => 25/10/2002: Cygwin support 63 | 64 | 0.4 => 19/09/2002: Added more serial ports (total of 8 ports) 65 | 66 | 0.3 => 15/03/2002: Damn, another bug found 67 | 68 | 0.2 => 14/03/2002: A bug fixed (read() was not blocking) 69 | 70 | 0.1 => 14/03/2002: First release 71 | -------------------------------------------------------------------------------- /ext/native/win_serialport_impl.c: -------------------------------------------------------------------------------- 1 | /* Ruby/SerialPort 2 | * Guillaume Pierronnet 3 | * Alan Stern 4 | * Daniel E. Shipton 5 | * Hector G. Parra 6 | * 7 | * This code is hereby licensed for public consumption under either the 8 | * GNU GPL v2 or greater. 9 | * 10 | * You should have received a copy of the GNU General Public License 11 | * along with this program; if not, write to the Free Software 12 | * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. 13 | * 14 | * For documentation on serial programming, see the excellent: 15 | * "Serial Programming Guide for POSIX Operating Systems" 16 | * written Michael R. Sweet. 17 | * http://www.easysw.com/~mike/serial/ 18 | */ 19 | 20 | #include "serialport.h" 21 | 22 | #if defined(OS_MSWIN) || defined(OS_BCCWIN) || defined(OS_MINGW) 23 | 24 | #include /* Standard input/output definitions */ 25 | #include /* Low-level I/O definitions */ 26 | #include /* File control definitions */ 27 | #include /* Windows standard function definitions */ 28 | 29 | 30 | static char sGetCommState[] = "GetCommState"; 31 | static char sSetCommState[] = "SetCommState"; 32 | static char sGetCommTimeouts[] = "GetCommTimeouts"; 33 | static char sSetCommTimeouts[] = "SetCommTimeouts"; 34 | 35 | 36 | static HANDLE get_handle_helper(VALUE io) 37 | { 38 | #ifdef HAVE_RB_IO_DESCRIPTOR 39 | return (HANDLE) _get_osfhandle(rb_io_descriptor(io)); 40 | #else 41 | rb_io_t* fp; 42 | GetOpenFile(io, fp); 43 | return (HANDLE) _get_osfhandle(fp->fd); 44 | #endif 45 | } 46 | 47 | /* hack to work around the fact that Ruby doesn't use GetLastError? */ 48 | static NORETURN( void _rb_win32_fail(const char *function_call) { 49 | rb_raise( 50 | rb_eRuntimeError, 51 | "%s failed: GetLastError returns %lu", 52 | function_call, GetLastError( ) 53 | ); 54 | }) 55 | 56 | VALUE RB_SERIAL_EXPORT sp_create_impl(VALUE class, VALUE _port) 57 | { 58 | int fd; 59 | HANDLE fh; 60 | int num_port; 61 | char *str_port; 62 | char port[260]; /* Windows XP MAX_PATH. See http://msdn.microsoft.com/en-us/library/aa365247(VS.85).aspx */ 63 | VALUE sp; 64 | 65 | DCB dcb; 66 | 67 | switch(TYPE(_port)) 68 | { 69 | case T_FIXNUM: 70 | num_port = FIX2INT(_port); 71 | if (num_port < 0) 72 | { 73 | rb_raise(rb_eArgError, "illegal port number"); 74 | } 75 | snprintf(port, sizeof(port) - 1, "\\\\.\\COM%d", num_port + 1); /* '0' is actually COM1, etc. */ 76 | port[sizeof(port) - 1] = 0; 77 | 78 | break; 79 | 80 | case T_STRING: 81 | str_port = StringValueCStr(_port); 82 | if (str_port[0] != '\\') /* Check for Win32 Device Namespace prefix "\\.\" */ 83 | { 84 | snprintf(port, sizeof(port) - 1, "\\\\.\\%s", str_port); 85 | port[sizeof(port) - 1] = 0; 86 | } 87 | else 88 | { 89 | snprintf(port, sizeof(port) - 1, "%s", str_port); 90 | port[sizeof(port) - 1] = 0; 91 | } 92 | break; 93 | 94 | default: 95 | rb_raise(rb_eTypeError, "wrong argument type"); 96 | break; 97 | } 98 | 99 | fd = open(port, O_BINARY | O_RDWR); 100 | if (fd == -1) 101 | { 102 | rb_sys_fail(port); 103 | } 104 | 105 | fh = (HANDLE) _get_osfhandle(fd); 106 | if (SetupComm(fh, 1024, 1024) == 0) 107 | { 108 | close(fd); 109 | rb_raise(rb_eArgError, "not a serial port"); 110 | } 111 | 112 | dcb.DCBlength = sizeof(dcb); 113 | if (GetCommState(fh, &dcb) == 0) 114 | { 115 | close(fd); 116 | _rb_win32_fail(sGetCommState); 117 | } 118 | dcb.fBinary = TRUE; 119 | dcb.fParity = FALSE; 120 | dcb.fOutxDsrFlow = FALSE; 121 | dcb.fDtrControl = DTR_CONTROL_ENABLE; 122 | dcb.fDsrSensitivity = FALSE; 123 | dcb.fTXContinueOnXoff = FALSE; 124 | dcb.fErrorChar = FALSE; 125 | dcb.fNull = FALSE; 126 | dcb.fAbortOnError = FALSE; 127 | dcb.XonChar = 17; 128 | dcb.XoffChar = 19; 129 | if (SetCommState(fh, &dcb) == 0) 130 | { 131 | close(fd); 132 | _rb_win32_fail(sSetCommState); 133 | } 134 | 135 | errno = 0; 136 | sp = rb_io_open_descriptor(class, fd, FMODE_READWRITE | FMODE_BINMODE | FMODE_SYNC, Qnil, Qnil, NULL); 137 | return sp; 138 | } 139 | 140 | VALUE RB_SERIAL_EXPORT sp_set_modem_params_impl(int argc, VALUE *argv, VALUE self) 141 | { 142 | HANDLE fh; 143 | DCB dcb; 144 | VALUE _data_rate, _data_bits, _parity, _stop_bits; 145 | int use_hash = 0; 146 | int data_rate, data_bits, parity; 147 | 148 | if (argc == 0) 149 | { 150 | return self; 151 | } 152 | if (argc == 1 && T_HASH == TYPE(argv[0])) 153 | { 154 | use_hash = 1; 155 | _data_rate = rb_hash_aref(argv[0], sBaud); 156 | _data_bits = rb_hash_aref(argv[0], sDataBits); 157 | _stop_bits = rb_hash_aref(argv[0], sStopBits); 158 | _parity = rb_hash_aref(argv[0], sParity); 159 | } 160 | 161 | fh = get_handle_helper(self); 162 | dcb.DCBlength = sizeof(dcb); 163 | if (GetCommState(fh, &dcb) == 0) 164 | { 165 | _rb_win32_fail(sGetCommState); 166 | } 167 | 168 | if (!use_hash) 169 | { 170 | _data_rate = argv[0]; 171 | } 172 | 173 | if (NIL_P(_data_rate)) 174 | { 175 | goto SkipDataRate; 176 | } 177 | 178 | Check_Type(_data_rate, T_FIXNUM); 179 | 180 | data_rate = FIX2INT(_data_rate); 181 | dcb.BaudRate = data_rate; 182 | 183 | SkipDataRate: 184 | 185 | if (!use_hash) 186 | { 187 | _data_bits = (argc >= 2 ? argv[1] : INT2FIX(8)); 188 | } 189 | 190 | if (NIL_P(_data_bits)) 191 | { 192 | goto SkipDataBits; 193 | } 194 | 195 | Check_Type(_data_bits, T_FIXNUM); 196 | 197 | data_bits = FIX2INT(_data_bits); 198 | if (4 <= data_bits && data_bits <= 8) 199 | { 200 | dcb.ByteSize = data_bits; 201 | } 202 | else 203 | { 204 | rb_raise(rb_eArgError, "unknown character size"); 205 | } 206 | 207 | SkipDataBits: 208 | 209 | if (!use_hash) 210 | { 211 | _stop_bits = (argc >= 3 ? argv[2] : INT2FIX(1)); 212 | } 213 | 214 | if (NIL_P(_stop_bits)) 215 | { 216 | goto SkipStopBits; 217 | } 218 | 219 | Check_Type(_stop_bits, T_FIXNUM); 220 | 221 | switch (FIX2INT(_stop_bits)) 222 | { 223 | case 1: 224 | dcb.StopBits = ONESTOPBIT; 225 | break; 226 | case 2: 227 | dcb.StopBits = TWOSTOPBITS; 228 | break; 229 | default: 230 | rb_raise(rb_eArgError, "unknown number of stop bits"); 231 | break; 232 | } 233 | 234 | SkipStopBits: 235 | 236 | if (!use_hash) 237 | { 238 | _parity = (argc >= 4 ? argv[3] : (dcb.ByteSize == 8 ? 239 | INT2FIX(NOPARITY) : INT2FIX(EVENPARITY))); 240 | } 241 | 242 | if (NIL_P(_parity)) 243 | { 244 | goto SkipParity; 245 | } 246 | 247 | Check_Type(_parity, T_FIXNUM); 248 | 249 | parity = FIX2INT(_parity); 250 | switch (parity) 251 | { 252 | case EVENPARITY: 253 | case ODDPARITY: 254 | case MARKPARITY: 255 | case SPACEPARITY: 256 | case NOPARITY: 257 | dcb.Parity = parity; 258 | break; 259 | 260 | default: 261 | rb_raise(rb_eArgError, "unknown parity"); 262 | break; 263 | } 264 | 265 | SkipParity: 266 | 267 | if (SetCommState(fh, &dcb) == 0) 268 | { 269 | _rb_win32_fail(sSetCommState); 270 | } 271 | 272 | return argv[0]; 273 | } 274 | 275 | void RB_SERIAL_EXPORT get_modem_params_impl(VALUE self, struct modem_params *mp) 276 | { 277 | HANDLE fh; 278 | DCB dcb; 279 | 280 | fh = get_handle_helper(self); 281 | dcb.DCBlength = sizeof(dcb); 282 | if (GetCommState(fh, &dcb) == 0) 283 | { 284 | _rb_win32_fail(sGetCommState); 285 | } 286 | 287 | mp->data_rate = dcb.BaudRate; 288 | mp->data_bits = dcb.ByteSize; 289 | mp->stop_bits = (dcb.StopBits == ONESTOPBIT ? 1 : 2); 290 | mp->parity = dcb.Parity; 291 | } 292 | 293 | VALUE RB_SERIAL_EXPORT sp_set_flow_control_impl(VALUE self, VALUE val) 294 | { 295 | HANDLE fh; 296 | int flowc; 297 | DCB dcb; 298 | 299 | Check_Type(val, T_FIXNUM); 300 | 301 | fh = get_handle_helper(self); 302 | dcb.DCBlength = sizeof(dcb); 303 | if (GetCommState(fh, &dcb) == 0) 304 | { 305 | _rb_win32_fail(sGetCommState); 306 | } 307 | 308 | flowc = FIX2INT(val); 309 | if (flowc & HARD) 310 | { 311 | dcb.fRtsControl = RTS_CONTROL_HANDSHAKE; 312 | dcb.fOutxCtsFlow = TRUE; 313 | } 314 | else 315 | { 316 | dcb.fRtsControl = RTS_CONTROL_ENABLE; 317 | dcb.fOutxCtsFlow = FALSE; 318 | } 319 | 320 | if (flowc & SOFT) 321 | { 322 | dcb.fOutX = dcb.fInX = TRUE; 323 | } 324 | else 325 | { 326 | dcb.fOutX = dcb.fInX = FALSE; 327 | } 328 | 329 | if (SetCommState(fh, &dcb) == 0) 330 | { 331 | _rb_win32_fail(sSetCommState); 332 | } 333 | 334 | return val; 335 | } 336 | 337 | VALUE RB_SERIAL_EXPORT sp_get_flow_control_impl(VALUE self) 338 | { 339 | HANDLE fh; 340 | int ret; 341 | DCB dcb; 342 | 343 | fh = get_handle_helper(self); 344 | dcb.DCBlength = sizeof(dcb); 345 | if (GetCommState(fh, &dcb) == 0) 346 | { 347 | _rb_win32_fail(sGetCommState); 348 | } 349 | 350 | ret = 0; 351 | if (dcb.fOutxCtsFlow) 352 | { 353 | ret += HARD; 354 | } 355 | 356 | if (dcb.fOutX) 357 | { 358 | ret += SOFT; 359 | } 360 | 361 | return INT2FIX(ret); 362 | } 363 | 364 | VALUE RB_SERIAL_EXPORT sp_set_read_timeout_impl(VALUE self, VALUE val) 365 | { 366 | int timeout; 367 | HANDLE fh; 368 | COMMTIMEOUTS ctout; 369 | 370 | Check_Type(val, T_FIXNUM); 371 | timeout = FIX2INT(val); 372 | 373 | fh = get_handle_helper(self); 374 | if (GetCommTimeouts(fh, &ctout) == 0) 375 | { 376 | _rb_win32_fail(sGetCommTimeouts); 377 | } 378 | 379 | if (timeout < 0) 380 | { 381 | ctout.ReadIntervalTimeout = MAXDWORD; 382 | ctout.ReadTotalTimeoutMultiplier = 0; 383 | ctout.ReadTotalTimeoutConstant = 0; 384 | } 385 | else if (timeout == 0) 386 | { 387 | ctout.ReadIntervalTimeout = MAXDWORD; 388 | ctout.ReadTotalTimeoutMultiplier = MAXDWORD; 389 | ctout.ReadTotalTimeoutConstant = MAXDWORD - 1; 390 | } 391 | else 392 | { 393 | ctout.ReadIntervalTimeout = timeout; 394 | ctout.ReadTotalTimeoutMultiplier = 0; 395 | ctout.ReadTotalTimeoutConstant = timeout; 396 | } 397 | 398 | if (SetCommTimeouts(fh, &ctout) == 0) 399 | { 400 | _rb_win32_fail(sSetCommTimeouts); 401 | } 402 | 403 | return val; 404 | } 405 | 406 | VALUE RB_SERIAL_EXPORT sp_get_read_timeout_impl(VALUE self) 407 | { 408 | HANDLE fh; 409 | COMMTIMEOUTS ctout; 410 | 411 | fh = get_handle_helper(self); 412 | if (GetCommTimeouts(fh, &ctout) == 0) 413 | { 414 | _rb_win32_fail(sGetCommTimeouts); 415 | } 416 | 417 | switch (ctout.ReadTotalTimeoutConstant) 418 | { 419 | case 0: 420 | return INT2FIX(-1); 421 | case MAXDWORD: 422 | return INT2FIX(0); 423 | } 424 | 425 | return INT2FIX(ctout.ReadTotalTimeoutConstant); 426 | } 427 | 428 | VALUE RB_SERIAL_EXPORT sp_set_write_timeout_impl(VALUE self, VALUE val) 429 | { 430 | int timeout; 431 | HANDLE fh; 432 | COMMTIMEOUTS ctout; 433 | 434 | Check_Type(val, T_FIXNUM); 435 | timeout = FIX2INT(val); 436 | 437 | fh = get_handle_helper(self); 438 | if (GetCommTimeouts(fh, &ctout) == 0) 439 | { 440 | _rb_win32_fail(sGetCommTimeouts); 441 | } 442 | 443 | if (timeout <= 0) 444 | { 445 | ctout.WriteTotalTimeoutMultiplier = 0; 446 | ctout.WriteTotalTimeoutConstant = 0; 447 | } 448 | else 449 | { 450 | ctout.WriteTotalTimeoutMultiplier = timeout; 451 | ctout.WriteTotalTimeoutConstant = 0; 452 | } 453 | 454 | if (SetCommTimeouts(fh, &ctout) == 0) 455 | { 456 | _rb_win32_fail(sSetCommTimeouts); 457 | } 458 | 459 | return val; 460 | } 461 | 462 | VALUE RB_SERIAL_EXPORT sp_get_write_timeout_impl(VALUE self) 463 | { 464 | HANDLE fh; 465 | COMMTIMEOUTS ctout; 466 | 467 | fh = get_handle_helper(self); 468 | if (GetCommTimeouts(fh, &ctout) == 0) 469 | { 470 | _rb_win32_fail(sGetCommTimeouts); 471 | } 472 | 473 | return INT2FIX(ctout.WriteTotalTimeoutMultiplier); 474 | } 475 | 476 | static void delay_ms(int time) 477 | { 478 | HANDLE ev; 479 | 480 | ev = CreateEvent(NULL, FALSE, FALSE, NULL); 481 | if (!ev) 482 | { 483 | _rb_win32_fail("CreateEvent"); 484 | } 485 | 486 | if (WaitForSingleObject(ev, time) == WAIT_FAILED) 487 | { 488 | _rb_win32_fail("WaitForSingleObject"); 489 | } 490 | 491 | CloseHandle(ev); 492 | } 493 | 494 | VALUE RB_SERIAL_EXPORT sp_break_impl(VALUE self, VALUE time) 495 | { 496 | HANDLE fh; 497 | 498 | Check_Type(time, T_FIXNUM); 499 | 500 | fh = get_handle_helper(self); 501 | if (SetCommBreak(fh) == 0) 502 | { 503 | _rb_win32_fail("SetCommBreak"); 504 | } 505 | 506 | delay_ms(FIX2INT(time) * 100); 507 | ClearCommBreak(fh); 508 | 509 | return Qnil; 510 | } 511 | 512 | void RB_SERIAL_EXPORT get_line_signals_helper_impl(VALUE obj, struct line_signals *ls) 513 | { 514 | HANDLE fh; 515 | unsigned long status; /* DWORD */ 516 | 517 | fh = get_handle_helper(obj); 518 | if (GetCommModemStatus(fh, &status) == 0) 519 | { 520 | _rb_win32_fail("GetCommModemStatus"); 521 | } 522 | 523 | ls->cts = (status & MS_CTS_ON ? 1 : 0); 524 | ls->dsr = (status & MS_DSR_ON ? 1 : 0); 525 | ls->dcd = (status & MS_RLSD_ON ? 1 : 0); 526 | ls->ri = (status & MS_RING_ON ? 1 : 0); 527 | } 528 | 529 | static VALUE set_signal(VALUE obj, VALUE val, int sigoff, int sigon) 530 | { 531 | HANDLE fh; 532 | int set, sig; 533 | 534 | Check_Type(val, T_FIXNUM); 535 | fh = get_handle_helper(obj); 536 | 537 | set = FIX2INT(val); 538 | if (set == 0) 539 | { 540 | sig = sigoff; 541 | } 542 | else if (set == 1) 543 | { 544 | sig = sigon; 545 | } 546 | else 547 | { 548 | rb_raise(rb_eArgError, "invalid value"); 549 | } 550 | 551 | if (EscapeCommFunction(fh, sig) == 0) 552 | { 553 | _rb_win32_fail("EscapeCommFunction"); 554 | } 555 | 556 | return val; 557 | } 558 | 559 | VALUE RB_SERIAL_EXPORT sp_set_rts_impl(VALUE self, VALUE val) 560 | { 561 | return set_signal(self, val, CLRRTS, SETRTS); 562 | } 563 | 564 | VALUE RB_SERIAL_EXPORT sp_set_dtr_impl(VALUE self, VALUE val) 565 | { 566 | return set_signal(self, val, CLRDTR, SETDTR); 567 | } 568 | 569 | VALUE RB_SERIAL_EXPORT sp_get_rts_impl(VALUE self) 570 | { 571 | rb_notimplement(); 572 | return self; 573 | } 574 | 575 | VALUE RB_SERIAL_EXPORT sp_get_dtr_impl(VALUE self) 576 | { 577 | rb_notimplement(); 578 | return self; 579 | } 580 | 581 | #ifndef PURGE_RXABORT 582 | #define PURGE_RXABORT 0x02 583 | #endif 584 | #ifndef PURGE_RXCLEAR 585 | #define PURGE_RXCLEAR 0x08 586 | #endif 587 | VALUE RB_SERIAL_EXPORT sp_flush_input_data_impl(VALUE self) 588 | { 589 | BOOL ret; 590 | HANDLE fh; 591 | 592 | fh = get_handle_helper(self); 593 | 594 | ret = PurgeComm(fh, (DWORD)(PURGE_RXCLEAR | PURGE_RXABORT)); 595 | if(!ret) { 596 | return Qfalse; 597 | } 598 | return Qtrue; 599 | } 600 | 601 | #ifndef PURGE_TXABORT 602 | #define PURGE_TXABORT 0x01 603 | #endif 604 | #ifndef PURGE_TXCLEAR 605 | #define PURGE_TXCLEAR 0x04 606 | #endif 607 | VALUE RB_SERIAL_EXPORT sp_flush_output_data_impl(VALUE self) 608 | { 609 | BOOL ret; 610 | HANDLE fh; 611 | 612 | fh = get_handle_helper(self); 613 | 614 | ret = PurgeComm(fh, (DWORD)(PURGE_TXCLEAR | PURGE_TXABORT)); 615 | if(!ret) { 616 | return Qfalse; 617 | } 618 | return Qtrue; 619 | } 620 | 621 | 622 | 623 | #endif /* defined(OS_MSWIN) || defined(OS_BCCWIN) || defined(OS_MINGW) */ 624 | -------------------------------------------------------------------------------- /ext/native/serialport.c: -------------------------------------------------------------------------------- 1 | /* Ruby/SerialPort 2 | * Guillaume Pierronnet 3 | * Alan Stern 4 | * Daniel E. Shipton 5 | * Jonas Bähr 6 | * Ryan C. Payne 7 | * 8 | * This code is hereby licensed for public consumption under either the 9 | * GNU GPL v2 or greater. 10 | * 11 | * You should have received a copy of the GNU General Public License 12 | * along with this program; if not, write to the Free Software 13 | * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. 14 | */ 15 | 16 | #include "serialport.h" 17 | 18 | VALUE cSerialPort; /* serial port class */ 19 | 20 | VALUE sBaud, sDataBits, sStopBits, sParity; /* strings */ 21 | VALUE sRts, sDtr, sCts, sDsr, sDcd, sRi; 22 | 23 | 24 | #ifndef HAVE_RB_IO_OPEN_DESCRIPTOR 25 | VALUE 26 | io_open_descriptor_fallback(VALUE klass, int descriptor, int mode, VALUE path, VALUE timeout, void *encoding) 27 | { 28 | VALUE arguments[2] = { 29 | (rb_update_max_fd(descriptor), INT2NUM(descriptor)), 30 | INT2FIX(mode), 31 | }; 32 | 33 | VALUE self = rb_class_new_instance(2, arguments, klass); 34 | 35 | rb_io_t *fptr; 36 | GetOpenFile(self, fptr); 37 | fptr->pathv = path; 38 | fptr->mode |= mode; 39 | 40 | return self; 41 | } 42 | #endif 43 | 44 | /* 45 | * @api private 46 | * 47 | * @see SerialPort#new 48 | * @see SerialPort#open 49 | */ 50 | static VALUE sp_create(VALUE class, VALUE _port) 51 | { 52 | return sp_create_impl(class, _port); 53 | } 54 | 55 | /* 56 | * Configure the serial port. You can pass a hash or multiple values 57 | * as separate arguments. Invalid or unsupported values will raise 58 | * an ArgumentError. 59 | * 60 | * When using a hash the following keys are recognized: 61 | * ["baud"] Integer from 50 to 256000, depending on platform 62 | * ["data_bits"] Integer from 5 to 8 (4 is allowed on Windows too) 63 | * ["stop_bits"] Integer, only allowed values are 1 or 2 (1.5 is not supported) 64 | * ["parity"] One of the constants NONE, EVEN or ODD (Windows allows also MARK and SPACE) 65 | * 66 | * When using separate arguments, they are interpreted as: 67 | * (baud, data_bits = 8, stop_bits = 1, parity = (previous_databits == 8 ? NONE : EVEN)) 68 | * A baudrate of nil will keep the old value. 69 | * The default parity depends on the number of databits configured before this function call. 70 | * 71 | * @overload set_modem_params(baud, data_bits, stop_bits, parity) 72 | * @param baud [Integer] the baud rate 73 | * @param data_bits [Integer] the number of data bits 74 | * @param stop_bits [Integer] the number of stop bits 75 | * @param parity [Integer] the type of parity checking 76 | * @overload set_modem_params(hash) 77 | * @param opts [Hash] the options to configure port 78 | * 79 | * @return [Hash] the original paramters 80 | * @raise [ArgumentError] if values are invalide or unsupported 81 | */ 82 | static VALUE sp_set_modem_params(int argc, VALUE *argv, VALUE self) 83 | { 84 | return sp_set_modem_params_impl(argc, argv, self); 85 | } 86 | 87 | /* 88 | * Send a break for the given time 89 | * 90 | * @param time [Integer] break time in tenths-of-a-second 91 | * @return [nil] 92 | * @note (POSIX) this value is very approximate 93 | */ 94 | static VALUE sp_break(VALUE self, VALUE time) 95 | { 96 | return sp_break_impl(self, time); 97 | } 98 | 99 | /* 100 | * Get the state of the DTR line 101 | * 102 | * @note (Windows) DTR is not available 103 | * @return [Integer] the state of DTR line, 0 or 1 104 | */ 105 | static VALUE sp_get_dtr(VALUE self) 106 | { 107 | return sp_get_dtr_impl(self); 108 | } 109 | 110 | /* 111 | * Get the flow control flag 112 | * 113 | * @return [Integer] the flow control flag 114 | * @see SerialPort#set_flow_control 115 | */ 116 | static VALUE sp_get_flow_control(VALUE self) 117 | { 118 | return sp_get_flow_control_impl(self); 119 | } 120 | 121 | /* 122 | * Get the read timeout value 123 | * 124 | * @return [Integer] the read timeout, in milliseconds 125 | * @see SerialPort#set_read_timeout 126 | */ 127 | static VALUE sp_get_read_timeout(VALUE self) 128 | { 129 | return sp_get_read_timeout_impl(self); 130 | } 131 | 132 | /* 133 | * Get the state of the RTS line 134 | * 135 | * @return [Integer] the state of RTS line, 0 or 1 136 | * @note (Windows) RTS is not available 137 | */ 138 | static VALUE sp_get_rts(VALUE self) 139 | { 140 | return sp_get_rts_impl(self); 141 | } 142 | 143 | /* 144 | * Get the write timeout 145 | * 146 | * @return [Integer] the write timeout, in milliseconds 147 | * @note (POSIX) write timeouts are not implemented 148 | */ 149 | static VALUE sp_get_write_timeout(VALUE self) 150 | { 151 | return sp_get_write_timeout_impl(self); 152 | } 153 | 154 | /* 155 | * Set the state of the DTR line 156 | * 157 | * @param val [Integer] the desired state of the DTR line, 0 or 1 158 | * @return [Integer] the original +val+ parameter 159 | */ 160 | static VALUE sp_set_dtr(VALUE self, VALUE val) 161 | { 162 | return sp_set_dtr_impl(self, val); 163 | } 164 | 165 | /* 166 | * Set the flow control 167 | * 168 | * @param val [Integer] the flow control flag, 169 | * +NONE+, +HARD+, +SOFT+, or (+HARD+ | +SOFT+) 170 | * @return [Integer] the original +val+ parameter 171 | * @note SerialPort::HARD mode is not supported on all platforms. 172 | * @note SerialPort::HARD uses RTS/CTS handshaking. 173 | * DSR/DTR is not supported. 174 | */ 175 | static VALUE sp_set_flow_control(VALUE self, VALUE val) 176 | { 177 | return sp_set_flow_control_impl(self, val); 178 | } 179 | 180 | /* 181 | * Set the timeout value (in milliseconds) for reading. 182 | * A negative read timeout will return all the available data without 183 | * waiting, a zero read timeout will not return until at least one 184 | * byte is available, and a positive read timeout returns when the 185 | * requested number of bytes is available or the interval between the 186 | * arrival of two bytes exceeds the timeout value. 187 | * 188 | * @param timeout [Integer] the read timeout in milliseconds 189 | * @return [Integer] the original +timeout+ parameter 190 | * @note Read timeouts don't mix well with multi-threading 191 | */ 192 | static VALUE sp_set_read_timeout(VALUE self, VALUE val) 193 | { 194 | return sp_set_read_timeout_impl(self, val); 195 | } 196 | 197 | /* 198 | * Set the state of the RTS line 199 | * 200 | * @param val [Integer] the state of RTS line, 0 or 1 201 | * @return [Integer] the original +val+ parameter 202 | */ 203 | static VALUE sp_set_rts(VALUE self, VALUE val) 204 | { 205 | return sp_set_rts_impl(self, val); 206 | } 207 | 208 | /* 209 | * Set a write timeout 210 | * 211 | * @param val [Integer] the write timeout in milliseconds 212 | * @return [Integer] the original +val+ parameter 213 | * @note (POSIX) write timeouts are not implemented 214 | */ 215 | static VALUE sp_set_write_timeout(VALUE self, VALUE val) 216 | { 217 | return sp_set_write_timeout_impl(self, val); 218 | } 219 | 220 | /* 221 | * @private helper 222 | */ 223 | static void get_modem_params(VALUE self, struct modem_params *mp) 224 | { 225 | get_modem_params_impl(self, mp); 226 | } 227 | 228 | /* 229 | * Set the baud rate 230 | * 231 | * @param data_rate [Integer] the baud rate 232 | * @return [Integer] the original +data_rate+ parameter 233 | * @see SerialPort#set_modem_params 234 | */ 235 | static VALUE sp_set_data_rate(VALUE self, VALUE data_rate) 236 | { 237 | VALUE argv[4]; 238 | 239 | argv[0] = data_rate; 240 | argv[1] = argv[2] = argv[3] = Qnil; 241 | sp_set_modem_params(4, argv, self); 242 | 243 | return data_rate; 244 | } 245 | 246 | /* 247 | * Set the data bits 248 | * 249 | * @param data_bits [Integer] the number of data bits 250 | * @return [Integer] the original +data_bits+ parameter 251 | * @see SerialPort#set_modem_params 252 | */ 253 | static VALUE sp_set_data_bits(VALUE self, VALUE data_bits) 254 | { 255 | VALUE argv[4]; 256 | 257 | argv[1] = data_bits; 258 | argv[0] = argv[2] = argv[3] = Qnil; 259 | sp_set_modem_params(4, argv, self); 260 | 261 | return data_bits; 262 | } 263 | 264 | /* 265 | * Set the stop bits 266 | * 267 | * @param stop_bits [Integer] the number of stop bits 268 | * @return [Integer] the original +stop_bits+ parameter 269 | * @see SerialPort#set_modem_params 270 | */ 271 | static VALUE sp_set_stop_bits(VALUE self, VALUE stop_bits) 272 | { 273 | VALUE argv[4]; 274 | 275 | argv[2] = stop_bits; 276 | argv[0] = argv[1] = argv[3] = Qnil; 277 | sp_set_modem_params(4, argv, self); 278 | 279 | return stop_bits; 280 | } 281 | 282 | /* 283 | * Set the parity 284 | * 285 | * @param parity [Integer] the parity type 286 | * @return [Integer] the original +parity+ parameter 287 | * @see SerialPort#set_modem_params 288 | */ 289 | static VALUE sp_set_parity(VALUE self, VALUE parity) 290 | { 291 | VALUE argv[4]; 292 | 293 | argv[3] = parity; 294 | argv[0] = argv[1] = argv[2] = Qnil; 295 | sp_set_modem_params(4, argv, self); 296 | 297 | return parity; 298 | } 299 | 300 | /* 301 | * Get the current baud rate 302 | * 303 | * @return [Integer] the current baud rate 304 | * @see SerialPort#set_modem_params 305 | */ 306 | static VALUE sp_get_data_rate(VALUE self) 307 | { 308 | struct modem_params mp; 309 | 310 | get_modem_params(self, &mp); 311 | 312 | return INT2FIX(mp.data_rate); 313 | } 314 | 315 | /* 316 | * Get the current data bits 317 | * 318 | * @return [Integer] the current number of data bits 319 | * @see SerialPort#set_modem_params 320 | */ 321 | static VALUE sp_get_data_bits(VALUE self) 322 | { 323 | struct modem_params mp; 324 | 325 | get_modem_params(self, &mp); 326 | 327 | return INT2FIX(mp.data_bits); 328 | } 329 | 330 | /* 331 | * Get the current stop bits 332 | * 333 | * @return [Integer] the current number of stop bits 334 | * @see SerialPort#set_modem_params for details 335 | */ 336 | static VALUE sp_get_stop_bits(VALUE self) 337 | { 338 | struct modem_params mp; 339 | 340 | get_modem_params(self, &mp); 341 | 342 | return INT2FIX(mp.stop_bits); 343 | } 344 | 345 | /* 346 | * Get the current parity 347 | * 348 | * @return [Integer] the current parity 349 | * @see SerialPort#set_modem_params 350 | */ 351 | static VALUE sp_get_parity(VALUE self) 352 | { 353 | struct modem_params mp; 354 | 355 | get_modem_params(self, &mp); 356 | 357 | return INT2FIX(mp.parity); 358 | } 359 | 360 | /* 361 | * Get the configure of the serial port 362 | * 363 | * @return [Hash] the serial port configuration 364 | * @see SerialPort#set_modem_params 365 | */ 366 | static VALUE sp_get_modem_params(VALUE self) 367 | { 368 | struct modem_params mp; 369 | VALUE hash; 370 | 371 | get_modem_params(self, &mp); 372 | 373 | hash = rb_hash_new(); 374 | 375 | rb_hash_aset(hash, sBaud, INT2FIX(mp.data_rate)); 376 | rb_hash_aset(hash, sDataBits, INT2FIX(mp.data_bits)); 377 | rb_hash_aset(hash, sStopBits, INT2FIX(mp.stop_bits)); 378 | rb_hash_aset(hash, sParity, INT2FIX(mp.parity)); 379 | 380 | return hash; 381 | } 382 | 383 | /* 384 | * @api private 385 | */ 386 | void get_line_signals_helper(VALUE obj, struct line_signals *ls) 387 | { 388 | get_line_signals_helper_impl(obj, ls); 389 | } 390 | 391 | /* 392 | * Get the state of the CTS line 393 | * 394 | * @return [Integer] the state of the CTS line, 0 or 1 395 | * @see SerialPort#get_signals 396 | */ 397 | static VALUE sp_get_cts(VALUE self) 398 | { 399 | struct line_signals ls; 400 | 401 | get_line_signals_helper(self, &ls); 402 | 403 | return INT2FIX(ls.cts); 404 | } 405 | 406 | /* 407 | * Get the state of the DSR line 408 | * 409 | * @return [Integer] the state of the DSR line, 0 or 1 410 | * @see SerialPort#get_signals 411 | */ 412 | static VALUE sp_get_dsr(VALUE self) 413 | { 414 | struct line_signals ls; 415 | 416 | get_line_signals_helper(self, &ls); 417 | 418 | return INT2FIX(ls.dsr); 419 | } 420 | 421 | /* 422 | * Get the state of the DCD line 423 | * 424 | * @return [Integer] the state of the DCD line, 0 or 1 425 | * @see SerialPort#get_signals 426 | */ 427 | static VALUE sp_get_dcd(VALUE self) 428 | { 429 | struct line_signals ls; 430 | 431 | get_line_signals_helper(self, &ls); 432 | 433 | return INT2FIX(ls.dcd); 434 | } 435 | 436 | /* 437 | * Get the state of the RI line 438 | * 439 | * @return [Integer] the state of the RI line, 0 or 1 440 | * @see SerialPort#get_signals 441 | */ 442 | static VALUE sp_get_ri(VALUE self) 443 | { 444 | struct line_signals ls; 445 | 446 | get_line_signals_helper(self, &ls); 447 | 448 | return INT2FIX(ls.ri); 449 | } 450 | 451 | /* 452 | * Return a hash with the state of each line status bit. 453 | * Keys: 454 | * "rts", "dtr", "cts", "dsr", "dcd", and "ri". 455 | * 456 | * @return [Hash] the state line info 457 | * @note (Windows) the rts and dtr values are not included 458 | * @note This method is implemented as both SerialPort#signals and SerialPort#get_signals 459 | */ 460 | static VALUE sp_signals(VALUE self) 461 | { 462 | struct line_signals ls; 463 | VALUE hash; 464 | 465 | get_line_signals_helper(self, &ls); 466 | 467 | hash = rb_hash_new(); 468 | 469 | #if !(defined(OS_MSWIN) || defined(OS_BCCWIN) || defined(OS_MINGW)) 470 | rb_hash_aset(hash, sRts, INT2FIX(ls.rts)); 471 | rb_hash_aset(hash, sDtr, INT2FIX(ls.dtr)); 472 | #endif 473 | rb_hash_aset(hash, sCts, INT2FIX(ls.cts)); 474 | rb_hash_aset(hash, sDsr, INT2FIX(ls.dsr)); 475 | rb_hash_aset(hash, sDcd, INT2FIX(ls.dcd)); 476 | rb_hash_aset(hash, sRi, INT2FIX(ls.ri)); 477 | 478 | return hash; 479 | } 480 | 481 | /** 482 | * Flush data received but not read. 483 | * 484 | * @return [Boolean] true on success or false if an error occurs. 485 | */ 486 | static VALUE sp_flush_input_data(VALUE self) 487 | { 488 | return sp_flush_input_data_impl(self); 489 | } 490 | 491 | /** 492 | * Flush data written but not transmitted. 493 | * 494 | * @return [Boolean] true on success or false if an error occurs. 495 | */ 496 | static VALUE sp_flush_output_data(VALUE self) 497 | { 498 | return sp_flush_output_data_impl(self); 499 | } 500 | 501 | 502 | void Init_serialport(void) 503 | { 504 | sBaud = rb_str_new2("baud"); 505 | sDataBits = rb_str_new2("data_bits"); 506 | sStopBits = rb_str_new2("stop_bits"); 507 | sParity = rb_str_new2("parity"); 508 | sRts = rb_str_new2("rts"); 509 | sDtr = rb_str_new2("dtr"); 510 | sCts = rb_str_new2("cts"); 511 | sDsr = rb_str_new2("dsr"); 512 | sDcd = rb_str_new2("dcd"); 513 | sRi = rb_str_new2("ri"); 514 | 515 | rb_gc_register_address(&sBaud); 516 | rb_gc_register_address(&sDataBits); 517 | rb_gc_register_address(&sStopBits); 518 | rb_gc_register_address(&sParity); 519 | rb_gc_register_address(&sRts); 520 | rb_gc_register_address(&sDtr); 521 | rb_gc_register_address(&sCts); 522 | rb_gc_register_address(&sDsr); 523 | rb_gc_register_address(&sDcd); 524 | rb_gc_register_address(&sRi); 525 | 526 | cSerialPort = rb_define_class("SerialPort", rb_cIO); 527 | rb_define_singleton_method(cSerialPort, "create", sp_create, 1); 528 | 529 | rb_define_method(cSerialPort, "get_modem_params", sp_get_modem_params, 0); 530 | rb_define_method(cSerialPort, "set_modem_params", sp_set_modem_params, -1); 531 | rb_define_method(cSerialPort, "modem_params", sp_get_modem_params, 0); 532 | rb_define_method(cSerialPort, "modem_params=", sp_set_modem_params, -1); 533 | rb_define_method(cSerialPort, "baud", sp_get_data_rate, 0); 534 | rb_define_method(cSerialPort, "baud=", sp_set_data_rate, 1); 535 | rb_define_method(cSerialPort, "data_bits", sp_get_data_bits, 0); 536 | rb_define_method(cSerialPort, "data_bits=", sp_set_data_bits, 1); 537 | rb_define_method(cSerialPort, "stop_bits", sp_get_stop_bits, 0); 538 | rb_define_method(cSerialPort, "stop_bits=", sp_set_stop_bits, 1); 539 | rb_define_method(cSerialPort, "parity", sp_get_parity, 0); 540 | rb_define_method(cSerialPort, "parity=", sp_set_parity, 1); 541 | 542 | rb_define_method(cSerialPort, "flow_control=", sp_set_flow_control, 1); 543 | rb_define_method(cSerialPort, "flow_control", sp_get_flow_control, 0); 544 | 545 | rb_define_method(cSerialPort, "read_timeout", sp_get_read_timeout, 0); 546 | rb_define_method(cSerialPort, "read_timeout=", sp_set_read_timeout, 1); 547 | rb_define_method(cSerialPort, "write_timeout", sp_get_write_timeout, 0); 548 | rb_define_method(cSerialPort, "write_timeout=", sp_set_write_timeout, 1); 549 | 550 | rb_define_method(cSerialPort, "break", sp_break, 1); 551 | 552 | rb_define_method(cSerialPort, "signals", sp_signals, 0); 553 | rb_define_method(cSerialPort, "get_signals", sp_signals, 0); 554 | rb_define_method(cSerialPort, "rts", sp_get_rts, 0); 555 | rb_define_method(cSerialPort, "rts=", sp_set_rts, 1); 556 | rb_define_method(cSerialPort, "dtr", sp_get_dtr, 0); 557 | rb_define_method(cSerialPort, "dtr=", sp_set_dtr, 1); 558 | rb_define_method(cSerialPort, "cts", sp_get_cts, 0); 559 | rb_define_method(cSerialPort, "dsr", sp_get_dsr, 0); 560 | rb_define_method(cSerialPort, "dcd", sp_get_dcd, 0); 561 | rb_define_method(cSerialPort, "ri", sp_get_ri, 0); 562 | 563 | rb_define_method(cSerialPort, "flush_input", sp_flush_input_data, 0); 564 | rb_define_method(cSerialPort, "flush_output", sp_flush_output_data, 0); 565 | 566 | /* 567 | * 0 568 | */ 569 | rb_define_const(cSerialPort, "NONE", INT2FIX(NONE)); 570 | 571 | /* 572 | * 1 573 | */ 574 | rb_define_const(cSerialPort, "HARD", INT2FIX(HARD)); 575 | 576 | /* 577 | * 2 578 | */ 579 | rb_define_const(cSerialPort, "SOFT", INT2FIX(SOFT)); 580 | 581 | /* 582 | * 0 583 | */ 584 | rb_define_const(cSerialPort, "SPACE", INT2FIX(SPACE)); 585 | 586 | /* 587 | * 1 588 | */ 589 | rb_define_const(cSerialPort, "MARK", INT2FIX(MARK)); 590 | 591 | /* 592 | * 2 593 | */ 594 | rb_define_const(cSerialPort, "EVEN", INT2FIX(EVEN)); 595 | 596 | /* 597 | * 3 598 | */ 599 | rb_define_const(cSerialPort, "ODD", INT2FIX(ODD)); 600 | } 601 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | GNU GENERAL PUBLIC LICENSE 2 | Version 2, June 1991 3 | 4 | Copyright (C) 1989, 1991 Free Software Foundation, Inc., 5 | 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA 6 | Everyone is permitted to copy and distribute verbatim copies 7 | of this license document, but changing it is not allowed. 8 | 9 | Preamble 10 | 11 | The licenses for most software are designed to take away your 12 | freedom to share and change it. By contrast, the GNU General Public 13 | License is intended to guarantee your freedom to share and change free 14 | software--to make sure the software is free for all its users. This 15 | General Public License applies to most of the Free Software 16 | Foundation's software and to any other program whose authors commit to 17 | using it. (Some other Free Software Foundation software is covered by 18 | the GNU Lesser General Public License instead.) You can apply it to 19 | your programs, too. 20 | 21 | When we speak of free software, we are referring to freedom, not 22 | price. Our General Public Licenses are designed to make sure that you 23 | have the freedom to distribute copies of free software (and charge for 24 | this service if you wish), that you receive source code or can get it 25 | if you want it, that you can change the software or use pieces of it 26 | in new free programs; and that you know you can do these things. 27 | 28 | To protect your rights, we need to make restrictions that forbid 29 | anyone to deny you these rights or to ask you to surrender the rights. 30 | These restrictions translate to certain responsibilities for you if you 31 | distribute copies of the software, or if you modify it. 32 | 33 | For example, if you distribute copies of such a program, whether 34 | gratis or for a fee, you must give the recipients all the rights that 35 | you have. You must make sure that they, too, receive or can get the 36 | source code. And you must show them these terms so they know their 37 | rights. 38 | 39 | We protect your rights with two steps: (1) copyright the software, and 40 | (2) offer you this license which gives you legal permission to copy, 41 | distribute and/or modify the software. 42 | 43 | Also, for each author's protection and ours, we want to make certain 44 | that everyone understands that there is no warranty for this free 45 | software. If the software is modified by someone else and passed on, we 46 | want its recipients to know that what they have is not the original, so 47 | that any problems introduced by others will not reflect on the original 48 | authors' reputations. 49 | 50 | Finally, any free program is threatened constantly by software 51 | patents. We wish to avoid the danger that redistributors of a free 52 | program will individually obtain patent licenses, in effect making the 53 | program proprietary. To prevent this, we have made it clear that any 54 | patent must be licensed for everyone's free use or not licensed at all. 55 | 56 | The precise terms and conditions for copying, distribution and 57 | modification follow. 58 | 59 | GNU GENERAL PUBLIC LICENSE 60 | TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 61 | 62 | 0. This License applies to any program or other work which contains 63 | a notice placed by the copyright holder saying it may be distributed 64 | under the terms of this General Public License. The "Program", below, 65 | refers to any such program or work, and a "work based on the Program" 66 | means either the Program or any derivative work under copyright law: 67 | that is to say, a work containing the Program or a portion of it, 68 | either verbatim or with modifications and/or translated into another 69 | language. (Hereinafter, translation is included without limitation in 70 | the term "modification".) Each licensee is addressed as "you". 71 | 72 | Activities other than copying, distribution and modification are not 73 | covered by this License; they are outside its scope. The act of 74 | running the Program is not restricted, and the output from the Program 75 | is covered only if its contents constitute a work based on the 76 | Program (independent of having been made by running the Program). 77 | Whether that is true depends on what the Program does. 78 | 79 | 1. You may copy and distribute verbatim copies of the Program's 80 | source code as you receive it, in any medium, provided that you 81 | conspicuously and appropriately publish on each copy an appropriate 82 | copyright notice and disclaimer of warranty; keep intact all the 83 | notices that refer to this License and to the absence of any warranty; 84 | and give any other recipients of the Program a copy of this License 85 | along with the Program. 86 | 87 | You may charge a fee for the physical act of transferring a copy, and 88 | you may at your option offer warranty protection in exchange for a fee. 89 | 90 | 2. You may modify your copy or copies of the Program or any portion 91 | of it, thus forming a work based on the Program, and copy and 92 | distribute such modifications or work under the terms of Section 1 93 | above, provided that you also meet all of these conditions: 94 | 95 | a) You must cause the modified files to carry prominent notices 96 | stating that you changed the files and the date of any change. 97 | 98 | b) You must cause any work that you distribute or publish, that in 99 | whole or in part contains or is derived from the Program or any 100 | part thereof, to be licensed as a whole at no charge to all third 101 | parties under the terms of this License. 102 | 103 | c) If the modified program normally reads commands interactively 104 | when run, you must cause it, when started running for such 105 | interactive use in the most ordinary way, to print or display an 106 | announcement including an appropriate copyright notice and a 107 | notice that there is no warranty (or else, saying that you provide 108 | a warranty) and that users may redistribute the program under 109 | these conditions, and telling the user how to view a copy of this 110 | License. (Exception: if the Program itself is interactive but 111 | does not normally print such an announcement, your work based on 112 | the Program is not required to print an announcement.) 113 | 114 | These requirements apply to the modified work as a whole. If 115 | identifiable sections of that work are not derived from the Program, 116 | and can be reasonably considered independent and separate works in 117 | themselves, then this License, and its terms, do not apply to those 118 | sections when you distribute them as separate works. But when you 119 | distribute the same sections as part of a whole which is a work based 120 | on the Program, the distribution of the whole must be on the terms of 121 | this License, whose permissions for other licensees extend to the 122 | entire whole, and thus to each and every part regardless of who wrote it. 123 | 124 | Thus, it is not the intent of this section to claim rights or contest 125 | your rights to work written entirely by you; rather, the intent is to 126 | exercise the right to control the distribution of derivative or 127 | collective works based on the Program. 128 | 129 | In addition, mere aggregation of another work not based on the Program 130 | with the Program (or with a work based on the Program) on a volume of 131 | a storage or distribution medium does not bring the other work under 132 | the scope of this License. 133 | 134 | 3. You may copy and distribute the Program (or a work based on it, 135 | under Section 2) in object code or executable form under the terms of 136 | Sections 1 and 2 above provided that you also do one of the following: 137 | 138 | a) Accompany it with the complete corresponding machine-readable 139 | source code, which must be distributed under the terms of Sections 140 | 1 and 2 above on a medium customarily used for software interchange; or, 141 | 142 | b) Accompany it with a written offer, valid for at least three 143 | years, to give any third party, for a charge no more than your 144 | cost of physically performing source distribution, a complete 145 | machine-readable copy of the corresponding source code, to be 146 | distributed under the terms of Sections 1 and 2 above on a medium 147 | customarily used for software interchange; or, 148 | 149 | c) Accompany it with the information you received as to the offer 150 | to distribute corresponding source code. (This alternative is 151 | allowed only for noncommercial distribution and only if you 152 | received the program in object code or executable form with such 153 | an offer, in accord with Subsection b above.) 154 | 155 | The source code for a work means the preferred form of the work for 156 | making modifications to it. For an executable work, complete source 157 | code means all the source code for all modules it contains, plus any 158 | associated interface definition files, plus the scripts used to 159 | control compilation and installation of the executable. However, as a 160 | special exception, the source code distributed need not include 161 | anything that is normally distributed (in either source or binary 162 | form) with the major components (compiler, kernel, and so on) of the 163 | operating system on which the executable runs, unless that component 164 | itself accompanies the executable. 165 | 166 | If distribution of executable or object code is made by offering 167 | access to copy from a designated place, then offering equivalent 168 | access to copy the source code from the same place counts as 169 | distribution of the source code, even though third parties are not 170 | compelled to copy the source along with the object code. 171 | 172 | 4. You may not copy, modify, sublicense, or distribute the Program 173 | except as expressly provided under this License. Any attempt 174 | otherwise to copy, modify, sublicense or distribute the Program is 175 | void, and will automatically terminate your rights under this License. 176 | However, parties who have received copies, or rights, from you under 177 | this License will not have their licenses terminated so long as such 178 | parties remain in full compliance. 179 | 180 | 5. You are not required to accept this License, since you have not 181 | signed it. However, nothing else grants you permission to modify or 182 | distribute the Program or its derivative works. These actions are 183 | prohibited by law if you do not accept this License. Therefore, by 184 | modifying or distributing the Program (or any work based on the 185 | Program), you indicate your acceptance of this License to do so, and 186 | all its terms and conditions for copying, distributing or modifying 187 | the Program or works based on it. 188 | 189 | 6. Each time you redistribute the Program (or any work based on the 190 | Program), the recipient automatically receives a license from the 191 | original licensor to copy, distribute or modify the Program subject to 192 | these terms and conditions. You may not impose any further 193 | restrictions on the recipients' exercise of the rights granted herein. 194 | You are not responsible for enforcing compliance by third parties to 195 | this License. 196 | 197 | 7. If, as a consequence of a court judgment or allegation of patent 198 | infringement or for any other reason (not limited to patent issues), 199 | conditions are imposed on you (whether by court order, agreement or 200 | otherwise) that contradict the conditions of this License, they do not 201 | excuse you from the conditions of this License. If you cannot 202 | distribute so as to satisfy simultaneously your obligations under this 203 | License and any other pertinent obligations, then as a consequence you 204 | may not distribute the Program at all. For example, if a patent 205 | license would not permit royalty-free redistribution of the Program by 206 | all those who receive copies directly or indirectly through you, then 207 | the only way you could satisfy both it and this License would be to 208 | refrain entirely from distribution of the Program. 209 | 210 | If any portion of this section is held invalid or unenforceable under 211 | any particular circumstance, the balance of the section is intended to 212 | apply and the section as a whole is intended to apply in other 213 | circumstances. 214 | 215 | It is not the purpose of this section to induce you to infringe any 216 | patents or other property right claims or to contest validity of any 217 | such claims; this section has the sole purpose of protecting the 218 | integrity of the free software distribution system, which is 219 | implemented by public license practices. Many people have made 220 | generous contributions to the wide range of software distributed 221 | through that system in reliance on consistent application of that 222 | system; it is up to the author/donor to decide if he or she is willing 223 | to distribute software through any other system and a licensee cannot 224 | impose that choice. 225 | 226 | This section is intended to make thoroughly clear what is believed to 227 | be a consequence of the rest of this License. 228 | 229 | 8. If the distribution and/or use of the Program is restricted in 230 | certain countries either by patents or by copyrighted interfaces, the 231 | original copyright holder who places the Program under this License 232 | may add an explicit geographical distribution limitation excluding 233 | those countries, so that distribution is permitted only in or among 234 | countries not thus excluded. In such case, this License incorporates 235 | the limitation as if written in the body of this License. 236 | 237 | 9. The Free Software Foundation may publish revised and/or new versions 238 | of the General Public License from time to time. Such new versions will 239 | be similar in spirit to the present version, but may differ in detail to 240 | address new problems or concerns. 241 | 242 | Each version is given a distinguishing version number. If the Program 243 | specifies a version number of this License which applies to it and "any 244 | later version", you have the option of following the terms and conditions 245 | either of that version or of any later version published by the Free 246 | Software Foundation. If the Program does not specify a version number of 247 | this License, you may choose any version ever published by the Free Software 248 | Foundation. 249 | 250 | 10. If you wish to incorporate parts of the Program into other free 251 | programs whose distribution conditions are different, write to the author 252 | to ask for permission. For software which is copyrighted by the Free 253 | Software Foundation, write to the Free Software Foundation; we sometimes 254 | make exceptions for this. Our decision will be guided by the two goals 255 | of preserving the free status of all derivatives of our free software and 256 | of promoting the sharing and reuse of software generally. 257 | 258 | NO WARRANTY 259 | 260 | 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY 261 | FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN 262 | OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES 263 | PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED 264 | OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF 265 | MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS 266 | TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE 267 | PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, 268 | REPAIR OR CORRECTION. 269 | 270 | 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING 271 | WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR 272 | REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, 273 | INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING 274 | OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED 275 | TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY 276 | YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER 277 | PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE 278 | POSSIBILITY OF SUCH DAMAGES. 279 | 280 | END OF TERMS AND CONDITIONS 281 | 282 | How to Apply These Terms to Your New Programs 283 | 284 | If you develop a new program, and you want it to be of the greatest 285 | possible use to the public, the best way to achieve this is to make it 286 | free software which everyone can redistribute and change under these terms. 287 | 288 | To do so, attach the following notices to the program. It is safest 289 | to attach them to the start of each source file to most effectively 290 | convey the exclusion of warranty; and each file should have at least 291 | the "copyright" line and a pointer to where the full notice is found. 292 | 293 | 294 | Copyright (C) 295 | 296 | This program is free software; you can redistribute it and/or modify 297 | it under the terms of the GNU General Public License as published by 298 | the Free Software Foundation; either version 2 of the License, or 299 | (at your option) any later version. 300 | 301 | This program is distributed in the hope that it will be useful, 302 | but WITHOUT ANY WARRANTY; without even the implied warranty of 303 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 304 | GNU General Public License for more details. 305 | 306 | You should have received a copy of the GNU General Public License along 307 | with this program; if not, write to the Free Software Foundation, Inc., 308 | 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. 309 | 310 | Also add information on how to contact you by electronic and paper mail. 311 | 312 | If the program is interactive, make it output a short notice like this 313 | when it starts in an interactive mode: 314 | 315 | Gnomovision version 69, Copyright (C) year name of author 316 | Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. 317 | This is free software, and you are welcome to redistribute it 318 | under certain conditions; type `show c' for details. 319 | 320 | The hypothetical commands `show w' and `show c' should show the appropriate 321 | parts of the General Public License. Of course, the commands you use may 322 | be called something other than `show w' and `show c'; they could even be 323 | mouse-clicks or menu items--whatever suits your program. 324 | 325 | You should also get your employer (if you work as a programmer) or your 326 | school, if any, to sign a "copyright disclaimer" for the program, if 327 | necessary. Here is a sample; alter the names: 328 | 329 | Yoyodyne, Inc., hereby disclaims all copyright interest in the program 330 | `Gnomovision' (which makes passes at compilers) written by James Hacker. 331 | 332 | , 1 April 1989 333 | Ty Coon, President of Vice 334 | 335 | This General Public License does not permit incorporating your program into 336 | proprietary programs. If your program is a subroutine library, you may 337 | consider it more useful to permit linking proprietary applications with the 338 | library. If this is what you want to do, use the GNU Lesser General 339 | Public License instead of this License. 340 | -------------------------------------------------------------------------------- /ext/native/posix_serialport_impl.c: -------------------------------------------------------------------------------- 1 | /* Ruby/SerialPort 2 | * Guillaume Pierronnet 3 | * Alan Stern 4 | * Daniel E. Shipton 5 | * Ryan C. Payne 6 | * Manuel "MaG" A. Güílamo 7 | * 8 | * This code is hereby licensed for public consumption under either the 9 | * GNU GPL v2 or greater. 10 | * 11 | * You should have received a copy of the GNU General Public License 12 | * along with this program; if not, write to the Free Software 13 | * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. 14 | * 15 | * For documentation on serial programming, see the excellent: 16 | * "Serial Programming Guide for POSIX Operating Systems" 17 | * written Michael R. Sweet. 18 | * http://www.easysw.com/~mike/serial/ 19 | */ 20 | 21 | #include "serialport.h" 22 | 23 | /* Check if we are on a posix compliant system. */ 24 | #if !defined(OS_MSWIN) && !defined(OS_BCCWIN) && !defined(OS_MINGW) 25 | 26 | #include /* Standard input/output definitions */ 27 | #include /* UNIX standard function definitions */ 28 | #include /* File control definitions */ 29 | #include /* Error number definitions */ 30 | #include /* POSIX terminal control definitions */ 31 | #include 32 | 33 | #ifdef CRTSCTS 34 | #define HAVE_FLOWCONTROL_HARD 1 35 | #else 36 | #undef HAVE_FLOWCONTROL_HARD 37 | #endif 38 | 39 | /* on mac os x, not all baud rates are defined in termios.h but 40 | they are mapped to the numeric value anyway, so we define them here */ 41 | #ifdef __APPLE__ 42 | #ifndef B460800 43 | #define B460800 460800 44 | #endif 45 | #ifndef B500000 46 | #define B500000 500000 47 | #endif 48 | #ifndef B576000 49 | #define B576000 576000 50 | #endif 51 | #ifndef B921600 52 | #define B921600 921600 53 | #endif 54 | #ifndef B1000000 55 | #define B1000000 1000000 56 | #endif 57 | #endif 58 | 59 | static char sTcgetattr[] = "tcgetattr"; 60 | static char sTcsetattr[] = "tcsetattr"; 61 | static char sIoctl[] = "ioctl"; 62 | 63 | 64 | int get_fd_helper(VALUE io) 65 | { 66 | #ifdef HAVE_RB_IO_DESCRIPTOR 67 | return rb_io_descriptor(io); 68 | #else 69 | rb_io_t* fp; 70 | GetOpenFile(io, fp); 71 | return fp->fd; 72 | #endif 73 | } 74 | 75 | VALUE sp_create_impl(VALUE class, VALUE _port) 76 | { 77 | int fd; 78 | long num_port; 79 | const char *port; 80 | const char *ports[] = { 81 | #if defined(OS_LINUX) || defined(OS_CYGWIN) 82 | "/dev/ttyS0", "/dev/ttyS1", "/dev/ttyS2", "/dev/ttyS3", 83 | "/dev/ttyS4", "/dev/ttyS5", "/dev/ttyS6", "/dev/ttyS7" 84 | #elif defined(OS_FREEBSD) || defined(OS_NETBSD) || defined(OS_OPENBSD) || defined(OS_DARWIN) 85 | "/dev/cuaa0", "/dev/cuaa1", "/dev/cuaa2", "/dev/cuaa3", 86 | "/dev/cuaa4", "/dev/cuaa5", "/dev/cuaa6", "/dev/cuaa7" 87 | #elif defined(OS_SOLARIS) 88 | "/dev/ttya", "/dev/ttyb", "/dev/ttyc", "/dev/ttyd", 89 | "/dev/ttye", "/dev/ttyf", "/dev/ttyg", "/dev/ttyh" 90 | #elif defined(OS_AIX) 91 | "/dev/tty0", "/dev/tty1", "/dev/tty2", "/dev/tty3", 92 | "/dev/tty4", "/dev/tty5", "/dev/tty6", "/dev/tty7" 93 | #elif defined(OS_IRIX) 94 | "/dev/ttyf1", "/dev/ttyf2", "/dev/ttyf3", "/dev/ttyf4", 95 | "/dev/ttyf5", "/dev/ttyf6", "/dev/ttyf7", "/dev/ttyf8" 96 | #endif 97 | }; 98 | struct termios params; 99 | VALUE sp; 100 | 101 | switch(TYPE(_port)) 102 | { 103 | case T_FIXNUM: 104 | num_port = FIX2LONG(_port); 105 | if (num_port < 0 || num_port > (long)(sizeof(ports) / sizeof(ports[0]))) 106 | { 107 | rb_raise(rb_eArgError, "illegal port number"); 108 | } 109 | port = ports[num_port]; 110 | break; 111 | 112 | case T_STRING: 113 | port = StringValueCStr(_port); 114 | break; 115 | 116 | default: 117 | rb_raise(rb_eTypeError, "wrong argument type"); 118 | break; 119 | } 120 | 121 | fd = open(port, O_RDWR | O_NOCTTY | O_NDELAY); 122 | if (fd == -1) 123 | { 124 | rb_sys_fail(port); 125 | } 126 | 127 | if (!isatty(fd)) 128 | { 129 | close(fd); 130 | rb_raise(rb_eArgError, "not a serial port"); 131 | } 132 | 133 | /* enable blocking read */ 134 | fcntl(fd, F_SETFL, fcntl(fd, F_GETFL, 0) & ~O_NONBLOCK); 135 | 136 | if (tcgetattr(fd, ¶ms) == -1) 137 | { 138 | close(fd); 139 | rb_sys_fail(sTcgetattr); 140 | } 141 | 142 | params.c_oflag = 0; 143 | params.c_lflag = 0; 144 | params.c_iflag &= (IXON | IXOFF | IXANY); 145 | params.c_cflag |= CLOCAL | CREAD; 146 | params.c_cflag &= ~HUPCL; 147 | 148 | if (tcsetattr(fd, TCSANOW, ¶ms) == -1) 149 | { 150 | close(fd); 151 | rb_sys_fail(sTcsetattr); 152 | } 153 | 154 | sp = rb_io_open_descriptor(class, fd, FMODE_READWRITE | FMODE_SYNC, Qnil, Qnil, NULL); 155 | 156 | return sp; 157 | } 158 | 159 | VALUE sp_set_modem_params_impl(int argc, VALUE *argv, VALUE self) 160 | { 161 | int fd; 162 | struct termios params; 163 | VALUE _data_rate, _data_bits, _parity, _stop_bits; 164 | int use_hash = 0; 165 | int data_rate, data_bits; 166 | _data_rate = _data_bits = _parity = _stop_bits = Qnil; 167 | 168 | if (argc == 0) 169 | { 170 | return self; 171 | } 172 | 173 | if (argc == 1 && T_HASH == TYPE(argv[0])) 174 | { 175 | use_hash = 1; 176 | _data_rate = rb_hash_aref(argv[0], sBaud); 177 | _data_bits = rb_hash_aref(argv[0], sDataBits); 178 | _stop_bits = rb_hash_aref(argv[0], sStopBits); 179 | _parity = rb_hash_aref(argv[0], sParity); 180 | } 181 | 182 | fd = get_fd_helper(self); 183 | if (tcgetattr(fd, ¶ms) == -1) 184 | { 185 | rb_sys_fail(sTcgetattr); 186 | } 187 | 188 | if (!use_hash) 189 | { 190 | _data_rate = argv[0]; 191 | } 192 | 193 | if (NIL_P(_data_rate)) 194 | { 195 | goto SkipDataRate; 196 | } 197 | Check_Type(_data_rate, T_FIXNUM); 198 | 199 | switch(FIX2INT(_data_rate)) 200 | { 201 | case 50: data_rate = B50; break; 202 | case 75: data_rate = B75; break; 203 | case 110: data_rate = B110; break; 204 | case 134: data_rate = B134; break; 205 | case 150: data_rate = B150; break; 206 | case 200: data_rate = B200; break; 207 | case 300: data_rate = B300; break; 208 | case 600: data_rate = B600; break; 209 | case 1200: data_rate = B1200; break; 210 | case 1800: data_rate = B1800; break; 211 | case 2400: data_rate = B2400; break; 212 | case 4800: data_rate = B4800; break; 213 | case 9600: data_rate = B9600; break; 214 | case 19200: data_rate = B19200; break; 215 | case 38400: data_rate = B38400; break; 216 | #ifdef B57600 217 | case 57600: data_rate = B57600; break; 218 | #endif 219 | #ifdef B76800 220 | case 76800: data_rate = B76800; break; 221 | #endif 222 | #ifdef B115200 223 | case 115200: data_rate = B115200; break; 224 | #endif 225 | #ifdef B230400 226 | case 230400: data_rate = B230400; break; 227 | #endif 228 | #ifdef B460800 229 | case 460800: data_rate = B460800; break; 230 | #endif 231 | #ifdef B500000 232 | case 500000: data_rate = B500000; break; 233 | #endif 234 | #ifdef B576000 235 | case 576000: data_rate = B576000; break; 236 | #endif 237 | #ifdef B921600 238 | case 921600: data_rate = B921600; break; 239 | #endif 240 | #ifdef B1000000 241 | case 1000000: data_rate = B1000000; break; 242 | #endif 243 | #ifdef B1500000 244 | case 1500000: data_rate = B1500000; break; 245 | #endif 246 | #ifdef B2000000 247 | case 2000000: data_rate = B2000000; break; 248 | #endif 249 | #ifdef B3000000 250 | case 3000000: data_rate = B3000000; break; 251 | #endif 252 | #ifdef B3500000 253 | case 3500000: data_rate = B3500000; break; 254 | #endif 255 | #ifdef B4000000 256 | case 4000000: data_rate = B4000000; break; 257 | #endif 258 | default: 259 | rb_raise(rb_eArgError, "unknown baud rate"); 260 | break; 261 | } 262 | cfsetispeed(¶ms, data_rate); 263 | cfsetospeed(¶ms, data_rate); 264 | 265 | SkipDataRate: 266 | 267 | if (!use_hash) 268 | { 269 | _data_bits = (argc >= 2 ? argv[1] : INT2FIX(8)); 270 | } 271 | 272 | if (NIL_P(_data_bits)) 273 | { 274 | goto SkipDataBits; 275 | } 276 | Check_Type(_data_bits, T_FIXNUM); 277 | 278 | switch(FIX2INT(_data_bits)) 279 | { 280 | case 5: 281 | data_bits = CS5; 282 | break; 283 | case 6: 284 | data_bits = CS6; 285 | break; 286 | case 7: 287 | data_bits = CS7; 288 | break; 289 | case 8: 290 | data_bits = CS8; 291 | break; 292 | default: 293 | rb_raise(rb_eArgError, "unknown character size"); 294 | break; 295 | } 296 | params.c_cflag &= ~CSIZE; 297 | params.c_cflag |= data_bits; 298 | 299 | SkipDataBits: 300 | 301 | if (!use_hash) 302 | { 303 | _stop_bits = (argc >= 3 ? argv[2] : INT2FIX(1)); 304 | } 305 | 306 | if (NIL_P(_stop_bits)) 307 | { 308 | goto SkipStopBits; 309 | } 310 | 311 | Check_Type(_stop_bits, T_FIXNUM); 312 | 313 | switch(FIX2INT(_stop_bits)) 314 | { 315 | case 1: 316 | params.c_cflag &= ~CSTOPB; 317 | break; 318 | case 2: 319 | params.c_cflag |= CSTOPB; 320 | break; 321 | default: 322 | rb_raise(rb_eArgError, "unknown number of stop bits"); 323 | break; 324 | } 325 | 326 | SkipStopBits: 327 | 328 | if (!use_hash) 329 | { 330 | _parity = (argc >= 4 ? argv[3] : ((params.c_cflag & CSIZE) == CS8 ? 331 | INT2FIX(NONE) : INT2FIX(EVEN))); 332 | } 333 | 334 | if (NIL_P(_parity)) 335 | { 336 | goto SkipParity; 337 | } 338 | 339 | Check_Type(_parity, T_FIXNUM); 340 | 341 | switch(FIX2INT(_parity)) 342 | { 343 | case EVEN: 344 | params.c_cflag |= PARENB; 345 | params.c_cflag &= ~PARODD; 346 | #ifdef CMSPAR 347 | params.c_cflag &= ~CMSPAR; 348 | #endif 349 | break; 350 | 351 | case ODD: 352 | params.c_cflag |= PARENB; 353 | params.c_cflag |= PARODD; 354 | #ifdef CMSPAR 355 | params.c_cflag &= ~CMSPAR; 356 | #endif 357 | break; 358 | 359 | #ifdef CMSPAR 360 | case SPACE: 361 | params.c_cflag |= PARENB; 362 | params.c_cflag &= ~PARODD; 363 | params.c_cflag |= CMSPAR; 364 | break; 365 | 366 | case MARK: 367 | params.c_cflag |= PARENB; 368 | params.c_cflag |= PARODD; 369 | params.c_cflag |= CMSPAR; 370 | break; 371 | #endif 372 | 373 | case NONE: 374 | params.c_cflag &= ~PARENB; 375 | #ifdef CMSPAR 376 | params.c_cflag &= ~CMSPAR; 377 | #endif 378 | break; 379 | 380 | default: 381 | rb_raise(rb_eArgError, "unknown parity"); 382 | break; 383 | } 384 | 385 | SkipParity: 386 | 387 | if (tcsetattr(fd, TCSANOW, ¶ms) == -1) 388 | { 389 | rb_sys_fail(sTcsetattr); 390 | } 391 | return argv[0]; 392 | } 393 | 394 | void get_modem_params_impl(VALUE self, struct modem_params *mp) 395 | { 396 | int fd; 397 | struct termios params; 398 | 399 | fd = get_fd_helper(self); 400 | if (tcgetattr(fd, ¶ms) == -1) 401 | { 402 | rb_sys_fail(sTcgetattr); 403 | } 404 | 405 | switch (cfgetospeed(¶ms)) 406 | { 407 | case B50: mp->data_rate = 50; break; 408 | case B75: mp->data_rate = 75; break; 409 | case B110: mp->data_rate = 110; break; 410 | case B134: mp->data_rate = 134; break; 411 | case B150: mp->data_rate = 150; break; 412 | case B200: mp->data_rate = 200; break; 413 | case B300: mp->data_rate = 300; break; 414 | case B600: mp->data_rate = 600; break; 415 | case B1200: mp->data_rate = 1200; break; 416 | case B1800: mp->data_rate = 1800; break; 417 | case B2400: mp->data_rate = 2400; break; 418 | case B4800: mp->data_rate = 4800; break; 419 | case B9600: mp->data_rate = 9600; break; 420 | case B19200: mp->data_rate = 19200; break; 421 | case B38400: mp->data_rate = 38400; break; 422 | #ifdef B57600 423 | case B57600: mp->data_rate = 57600; break; 424 | #endif 425 | #ifdef B76800 426 | case B76800: mp->data_rate = 76800; break; 427 | #endif 428 | #ifdef B115200 429 | case B115200: mp->data_rate = 115200; break; 430 | #endif 431 | #ifdef B230400 432 | case B230400: mp->data_rate = 230400; break; 433 | #endif 434 | #ifdef B460800 435 | case B460800: mp->data_rate = 460800; break; 436 | #endif 437 | #ifdef B500000 438 | case B500000: mp->data_rate = 500000; break; 439 | #endif 440 | #ifdef B576000 441 | case B576000: mp->data_rate = 576000; break; 442 | #endif 443 | #ifdef B921600 444 | case B921600: mp->data_rate = 921600; break; 445 | #endif 446 | #ifdef B1000000 447 | case B1000000: mp->data_rate = 1000000; break; 448 | #endif 449 | } 450 | 451 | switch(params.c_cflag & CSIZE) 452 | { 453 | case CS5: 454 | mp->data_bits = 5; 455 | break; 456 | case CS6: 457 | mp->data_bits = 6; 458 | break; 459 | case CS7: 460 | mp->data_bits = 7; 461 | break; 462 | case CS8: 463 | mp->data_bits = 8; 464 | break; 465 | default: 466 | mp->data_bits = 0; 467 | break; 468 | } 469 | 470 | mp->stop_bits = (params.c_cflag & CSTOPB ? 2 : 1); 471 | 472 | if (!(params.c_cflag & PARENB)) 473 | { 474 | mp->parity = NONE; 475 | } 476 | else if (params.c_cflag & PARODD) 477 | { 478 | mp->parity = ODD; 479 | } 480 | else 481 | { 482 | mp->parity = EVEN; 483 | } 484 | } 485 | 486 | VALUE sp_set_flow_control_impl(VALUE self, VALUE val) 487 | { 488 | int fd; 489 | int flowc; 490 | struct termios params; 491 | 492 | Check_Type(val, T_FIXNUM); 493 | 494 | fd = get_fd_helper(self); 495 | if (tcgetattr(fd, ¶ms) == -1) 496 | { 497 | rb_sys_fail(sTcgetattr); 498 | } 499 | 500 | flowc = FIX2INT(val); 501 | if (flowc & HARD) 502 | { 503 | #ifdef HAVE_FLOWCONTROL_HARD 504 | params.c_cflag |= CRTSCTS; 505 | } 506 | else 507 | { 508 | params.c_cflag &= ~CRTSCTS; 509 | } 510 | #else 511 | rb_raise(rb_eIOError, "Hardware flow control not supported"); 512 | } 513 | #endif 514 | 515 | if (flowc & SOFT) 516 | { 517 | params.c_iflag |= (IXON | IXOFF | IXANY); 518 | } 519 | else 520 | { 521 | params.c_iflag &= ~(IXON | IXOFF | IXANY); 522 | } 523 | 524 | if (tcsetattr(fd, TCSANOW, ¶ms) == -1) 525 | { 526 | rb_sys_fail(sTcsetattr); 527 | } 528 | 529 | return val; 530 | } 531 | 532 | VALUE sp_get_flow_control_impl(VALUE self) 533 | { 534 | int ret; 535 | int fd; 536 | struct termios params; 537 | 538 | fd = get_fd_helper(self); 539 | if (tcgetattr(fd, ¶ms) == -1) 540 | { 541 | rb_sys_fail(sTcgetattr); 542 | } 543 | 544 | ret = 0; 545 | 546 | #ifdef HAVE_FLOWCONTROL_HARD 547 | if (params.c_cflag & CRTSCTS) 548 | { 549 | ret += HARD; 550 | } 551 | #endif 552 | 553 | if (params.c_iflag & (IXON | IXOFF | IXANY)) 554 | { 555 | ret += SOFT; 556 | } 557 | 558 | return INT2FIX(ret); 559 | } 560 | 561 | VALUE sp_set_read_timeout_impl(VALUE self, VALUE val) 562 | { 563 | int timeout; 564 | int fd; 565 | struct termios params; 566 | 567 | Check_Type(val, T_FIXNUM); 568 | timeout = FIX2INT(val); 569 | 570 | fd = get_fd_helper(self); 571 | if (tcgetattr(fd, ¶ms) == -1) 572 | { 573 | rb_sys_fail(sTcgetattr); 574 | } 575 | 576 | if (timeout < 0) 577 | { 578 | params.c_cc[VTIME] = 0; 579 | params.c_cc[VMIN] = 0; 580 | } 581 | else if (timeout == 0) 582 | { 583 | params.c_cc[VTIME] = 0; 584 | params.c_cc[VMIN] = 1; 585 | } 586 | else 587 | { 588 | params.c_cc[VTIME] = (timeout + 50) / 100; 589 | params.c_cc[VMIN] = 0; 590 | } 591 | 592 | if (tcsetattr(fd, TCSANOW, ¶ms) == -1) 593 | { 594 | rb_sys_fail(sTcsetattr); 595 | } 596 | 597 | return val; 598 | } 599 | 600 | VALUE sp_get_read_timeout_impl(VALUE self) 601 | { 602 | int fd; 603 | struct termios params; 604 | 605 | fd = get_fd_helper(self); 606 | if (tcgetattr(fd, ¶ms) == -1) 607 | { 608 | rb_sys_fail(sTcgetattr); 609 | } 610 | 611 | if (params.c_cc[VTIME] == 0 && params.c_cc[VMIN] == 0) 612 | { 613 | return INT2FIX(-1); 614 | } 615 | 616 | return INT2FIX(params.c_cc[VTIME] * 100); 617 | } 618 | 619 | NORETURN(VALUE sp_set_write_timeout_impl(VALUE self, VALUE val)) 620 | { 621 | rb_notimplement(); 622 | } 623 | 624 | NORETURN(VALUE sp_get_write_timeout_impl(VALUE self)) 625 | { 626 | rb_notimplement(); 627 | } 628 | 629 | VALUE sp_break_impl(VALUE self, VALUE time) 630 | { 631 | int fd; 632 | 633 | Check_Type(time, T_FIXNUM); 634 | 635 | fd = get_fd_helper(self); 636 | 637 | if (tcsendbreak(fd, FIX2INT(time) / 3) == -1) 638 | { 639 | rb_sys_fail("tcsendbreak"); 640 | } 641 | 642 | return Qnil; 643 | } 644 | 645 | void get_line_signals_helper_impl(VALUE obj, struct line_signals *ls) 646 | { 647 | int fd, status; 648 | 649 | fd = get_fd_helper(obj); 650 | 651 | if (ioctl(fd, TIOCMGET, &status) == -1) 652 | { 653 | rb_sys_fail(sIoctl); 654 | } 655 | 656 | ls->rts = (status & TIOCM_RTS ? 1 : 0); 657 | ls->dtr = (status & TIOCM_DTR ? 1 : 0); 658 | ls->cts = (status & TIOCM_CTS ? 1 : 0); 659 | ls->dsr = (status & TIOCM_DSR ? 1 : 0); 660 | ls->dcd = (status & TIOCM_CD ? 1 : 0); 661 | ls->ri = (status & TIOCM_RI ? 1 : 0); 662 | } 663 | 664 | VALUE set_signal_impl(VALUE obj, VALUE val, int sig) 665 | { 666 | int status; 667 | int fd; 668 | int set; 669 | 670 | Check_Type(val, T_FIXNUM); 671 | fd = get_fd_helper(obj); 672 | 673 | if (ioctl(fd, TIOCMGET, &status) == -1) 674 | { 675 | rb_sys_fail(sIoctl); 676 | } 677 | 678 | set = FIX2INT(val); 679 | 680 | if (set == 0) 681 | { 682 | status &= ~sig; 683 | } 684 | else if (set == 1) 685 | { 686 | status |= sig; 687 | } 688 | else 689 | { 690 | rb_raise(rb_eArgError, "invalid value"); 691 | } 692 | 693 | if (ioctl(fd, TIOCMSET, &status) == -1) 694 | { 695 | rb_sys_fail(sIoctl); 696 | } 697 | 698 | return val; 699 | } 700 | 701 | VALUE sp_set_rts_impl(VALUE self, VALUE val) 702 | { 703 | return set_signal_impl(self, val, TIOCM_RTS); 704 | } 705 | 706 | VALUE sp_set_dtr_impl(VALUE self, VALUE val) 707 | { 708 | return set_signal_impl(self, val, TIOCM_DTR); 709 | } 710 | 711 | VALUE sp_get_rts_impl(VALUE self) 712 | { 713 | struct line_signals ls; 714 | 715 | get_line_signals_helper_impl(self, &ls); 716 | return INT2FIX(ls.rts); 717 | } 718 | 719 | VALUE sp_get_dtr_impl(VALUE self) 720 | { 721 | struct line_signals ls; 722 | 723 | get_line_signals_helper_impl(self, &ls); 724 | 725 | return INT2FIX(ls.dtr); 726 | } 727 | 728 | VALUE sp_flush_input_data_impl(VALUE self) 729 | { 730 | int fd; 731 | int ret; 732 | 733 | fd = get_fd_helper(self); 734 | 735 | ret = tcflush(fd, TCIFLUSH); 736 | if(ret<0) { 737 | return Qfalse; 738 | } 739 | 740 | return Qtrue; 741 | } 742 | 743 | VALUE sp_flush_output_data_impl(VALUE self) 744 | { 745 | int fd; 746 | int ret; 747 | 748 | fd = get_fd_helper(self); 749 | 750 | ret = tcflush(fd, TCOFLUSH); 751 | if(ret<0) { 752 | return Qfalse; 753 | } 754 | 755 | return Qtrue; 756 | } 757 | 758 | 759 | 760 | #endif /* !defined(OS_MSWIN) && !defined(OS_BCCWIN) && !defined(OS_MINGW) */ 761 | --------------------------------------------------------------------------------