rubyserial 0.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 768694b356264952b50a41408e6aecd0e2bd0f74
4
+ data.tar.gz: 6e88db468ceb626d7d66c48687873a42afb994ad
5
+ SHA512:
6
+ metadata.gz: e8610224155564d6a8acc8f3f922c3ce5ed0cc7fbcde57ff3aca9ccda649f24b53bbd1df5e1986d4175f4489f5a8ff48dd48f47ab3df2cbeefa075d53943376b
7
+ data.tar.gz: 6f8974af626202bb8a7f67ae28a5e25a62487cde2b06000dfcfab18d8b9fc39949dd7fcd0793ef474365c46f5f9186894abf2151c9bbe3078c06456cfe5da92c
@@ -0,0 +1,3 @@
1
+ .bundle
2
+ /vendor
3
+ Gemfile.lock
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ # A sample Gemfile
2
+ source "http://rubygems.org"
3
+
4
+ gemspec
data/LICENSE ADDED
@@ -0,0 +1,13 @@
1
+ Copyright (c) 2014 The Hybrid Group
2
+
3
+ Licensed under the Apache License, Version 2.0 (the "License");
4
+ you may not use this file except in compliance with the License.
5
+ You may obtain a copy of the License at
6
+
7
+ http://www.apache.org/licenses/LICENSE-2.0
8
+
9
+ Unless required by applicable law or agreed to in writing, software
10
+ distributed under the License is distributed on an "AS IS" BASIS,
11
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
+ See the License for the specific language governing permissions and
13
+ limitations under the License.
@@ -0,0 +1,27 @@
1
+ # rubyserial
2
+
3
+ A simple ruby gem which allows you to read and write from a serial port.
4
+
5
+ Usage:
6
+ ```ruby
7
+ require 'rubyserial'
8
+ s = Serial.new("/dev/ttyACM0", 57600)
9
+ ```
10
+
11
+ #####write(string) -> int
12
+ ```
13
+ returns the number of bytes written.
14
+ RubySerial::Exception on error.
15
+ ```
16
+ ######read(length) -> string
17
+ ```
18
+ returns a string up to "length".
19
+ read is not guarenteed to return the entire "length" specified.
20
+ returns "" on no data
21
+ RubySerial::Exception on error.
22
+ ```
23
+
24
+ ######RubySerial::Exception
25
+ ```
26
+ returns the underlying system error code
27
+ ```
@@ -0,0 +1,2 @@
1
+ require 'bundler'
2
+ Bundler::GemHelper.install_tasks
@@ -0,0 +1,21 @@
1
+ $:.unshift(File.dirname(__FILE__))
2
+ require 'rbconfig'
3
+ require 'ffi'
4
+ include RbConfig
5
+
6
+ module RubySerial
7
+ class Exception < Exception
8
+ end
9
+ end
10
+
11
+ if CONFIG['host_os'] =~ /mswin|windows|mingw/i
12
+ require 'rubyserial/windows_constants'
13
+ require 'rubyserial/windows'
14
+ else
15
+ if CONFIG['host_os'] =~ /linux/i
16
+ require 'rubyserial/linux_constants'
17
+ else
18
+ require 'rubyserial/osx_constants'
19
+ end
20
+ require 'rubyserial/posix'
21
+ end
@@ -0,0 +1,214 @@
1
+ require 'ffi'
2
+
3
+ module RubySerial
4
+ module Posix
5
+ extend FFI::Library
6
+ ffi_lib FFI::Library::LIBC
7
+
8
+ O_NONBLOCK = 00004000
9
+ O_NOCTTY = 00000400
10
+ O_RDWR = 00000002
11
+ F_GETFL = 3
12
+ F_SETFL = 4
13
+ VTIME = 5
14
+ TCSANOW = 0
15
+ TCSETS = 0x5402
16
+ IGNPAR = 0000004
17
+ CREAD = 0000200
18
+ CLOCAL = 0004000
19
+ VMIN = 6
20
+ NCCS = 19
21
+
22
+ DATA_BITS = {
23
+ 5 => 0000000,
24
+ 6 => 0000020,
25
+ 7 => 0000040,
26
+ 8 => 0000060
27
+ }
28
+
29
+ BAUDE_RATES = {
30
+ 0 => 0000000,
31
+ 50 => 0000001,
32
+ 75 => 0000002,
33
+ 110 => 0000003,
34
+ 134 => 0000004,
35
+ 150 => 0000005,
36
+ 200 => 0000006,
37
+ 300 => 0000007,
38
+ 600 => 0000010,
39
+ 1200 => 0000011,
40
+ 1800 => 0000012,
41
+ 2400 => 0000013,
42
+ 4800 => 0000014,
43
+ 9600 => 0000015,
44
+ 19200 => 0000016,
45
+ 38400 => 0000017,
46
+ 57600 => 0010001,
47
+ 115200 => 0010002,
48
+ 230400 => 0010003,
49
+ 460800 => 0010004,
50
+ 500000 => 0010005,
51
+ 576000 => 0010006,
52
+ 921600 => 0010007,
53
+ 1000000 => 0010010,
54
+ 1152000 => 0010011,
55
+ 1500000 => 0010012,
56
+ 2000000 => 0010013,
57
+ 2500000 => 0010014,
58
+ 3000000 => 0010015,
59
+ 3500000 => 0010016,
60
+ 4000000 => 0010017
61
+ }
62
+
63
+ ERROR_CODES = {
64
+ 1 => "EPERM",
65
+ 2 => "ENOENT",
66
+ 3 => "ESRCH",
67
+ 4 => "EINTR",
68
+ 5 => "EIO",
69
+ 6 => "ENXIO",
70
+ 7 => "E2BIG",
71
+ 8 => "ENOEXEC",
72
+ 9 => "EBADF",
73
+ 10 => "ECHILD",
74
+ 11 => "EAGAIN",
75
+ 12 => "ENOMEM",
76
+ 13 => "EACCES",
77
+ 14 => "EFAULT",
78
+ 15 => "ENOTBLK",
79
+ 16 => "EBUSY",
80
+ 17 => "EEXIST",
81
+ 18 => "EXDEV",
82
+ 19 => "ENODEV",
83
+ 20 => "ENOTDIR ",
84
+ 21 => "EISDIR",
85
+ 22 => "EINVAL",
86
+ 23 => "ENFILE",
87
+ 24 => "EMFILE",
88
+ 25 => "ENOTTY",
89
+ 26 => "ETXTBSY",
90
+ 27 => "EFBIG",
91
+ 28 => "ENOSPC",
92
+ 29 => "ESPIPE",
93
+ 30 => "EROFS",
94
+ 31 => "EMLINK",
95
+ 32 => "EPIPE",
96
+ 33 => "EDOM",
97
+ 34 => "ERANGE",
98
+ 35 => "EDEADLK",
99
+ 36 => "ENAMETOOLONG",
100
+ 37 => "ENOLCK ",
101
+ 38 => "ENOSYS",
102
+ 39 => "ENOTEMPTY",
103
+ 40 => "ELOOP",
104
+ 42 => "ENOMSG",
105
+ 43 => "EIDRM",
106
+ 44 => "ECHRNG",
107
+ 45 => "EL2NSYNC",
108
+ 46 => "EL3HLT",
109
+ 47 => "EL3RST",
110
+ 48 => "ELNRNG",
111
+ 49 => "EUNATCH",
112
+ 50 => "ENOCSI",
113
+ 51 => "EL2HLT",
114
+ 52 => "EBADE",
115
+ 53 => "EBADR",
116
+ 54 => "EXFULL",
117
+ 55 => "ENOANO",
118
+ 56 => "EBADRQC",
119
+ 57 => "EBADSLT",
120
+ 59 => "EBFONT",
121
+ 60 => "ENOSTR",
122
+ 61 => "ENODATA",
123
+ 62 => "ETIME",
124
+ 63 => "ENOSR",
125
+ 64 => "ENONET",
126
+ 65 => "ENOPKG",
127
+ 66 => "EREMOTE",
128
+ 67 => "ENOLINK",
129
+ 68 => "EADV",
130
+ 69 => "ESRMNT",
131
+ 70 => "ECOMM",
132
+ 71 => "EPROTO",
133
+ 72 => "EMULTIHOP",
134
+ 73 => "EDOTDOT",
135
+ 74 => "EBADMSG",
136
+ 75 => "EOVERFLOW",
137
+ 76 => "ENOTUNIQ",
138
+ 77 => "EBADFD",
139
+ 78 => "EREMCHG",
140
+ 79 => "ELIBACC",
141
+ 80 => "ELIBBAD",
142
+ 81 => "ELIBSCN",
143
+ 82 => "ELIBMAX",
144
+ 83 => "ELIBEXEC",
145
+ 84 => "EILSEQ",
146
+ 85 => "ERESTART",
147
+ 86 => "ESTRPIPE",
148
+ 87 => "EUSERS",
149
+ 88 => "ENOTSOCK",
150
+ 89 => "EDESTADDRREQ",
151
+ 90 => "EMSGSIZE",
152
+ 91 => "EPROTOTYPE",
153
+ 92 => "ENOPROTOOPT",
154
+ 93 => "EPROTONOSUPPORT",
155
+ 94 => "ESOCKTNOSUPPORT",
156
+ 95 => "EOPNOTSUPP",
157
+ 96 => "EPFNOSUPPORT",
158
+ 97 => "EAFNOSUPPORT",
159
+ 98 => "EADDRINUSE",
160
+ 99 => "EADDRNOTAVAIL",
161
+ 100 => "ENETDOWN",
162
+ 101 => "ENETUNREACH",
163
+ 102 => "ENETRESET",
164
+ 103 => "ECONNABORTED",
165
+ 104 => "ECONNRESET",
166
+ 105 => "ENOBUFS",
167
+ 106 => "EISCONN",
168
+ 107 => "ENOTCONN",
169
+ 108 => "ESHUTDOWN",
170
+ 109 => "ETOOMANYREFS",
171
+ 110 => "ETIMEDOUT",
172
+ 111 => "ECONNREFUSED",
173
+ 112 => "EHOSTDOWN",
174
+ 113 => "EHOSTUNREACH",
175
+ 114 => "EALREADY",
176
+ 115 => "EINPROGRESS",
177
+ 116 => "ESTALE",
178
+ 117 => "EUCLEAN",
179
+ 118 => "ENOTNAM",
180
+ 119 => "ENAVAIL",
181
+ 120 => "EISNAM",
182
+ 121 => "EREMOTEIO",
183
+ 122 => "EDQUOT",
184
+ 123 => "ENOMEDIUM",
185
+ 124 => "EMEDIUMTYPE",
186
+ 125 => "ECANCELED",
187
+ 126 => "ENOKEY",
188
+ 127 => "EKEYEXPIRED",
189
+ 128 => "EKEYREVOKED",
190
+ 129 => "EKEYREJECTED",
191
+ 130 => "EOWNERDEAD",
192
+ 131 => "ENOTRECOVERABLE"
193
+ }
194
+
195
+ class Termios < FFI::Struct
196
+ layout :c_iflag, :uint,
197
+ :c_oflag, :uint,
198
+ :c_cflag, :uint,
199
+ :c_lflag, :uint,
200
+ :c_line, :uchar,
201
+ :cc_c, [ :uchar, NCCS ],
202
+ :c_ispeed, :uint,
203
+ :c_ospeed, :uint
204
+ end
205
+
206
+ attach_function :ioctl, [ :int, :ulong, RubySerial::Posix::Termios], :int
207
+ attach_function :tcsetattr, [ :int, :int, RubySerial::Posix::Termios ], :int
208
+ attach_function :fcntl, [:int, :int, :varargs], :int
209
+ attach_function :open, [:pointer, :int], :int
210
+ attach_function :close, [:int], :int
211
+ attach_function :write, [:int, :pointer, :int],:int
212
+ attach_function :read, [:int, :pointer, :int],:int
213
+ end
214
+ end
@@ -0,0 +1,181 @@
1
+ require 'ffi'
2
+
3
+ module RubySerial
4
+ module Posix
5
+ extend FFI::Library
6
+ ffi_lib FFI::Library::LIBC
7
+
8
+ O_RDWR = 0x0002
9
+ O_NOCTTY = 0x20000
10
+ O_NONBLOCK = 0x0004
11
+ F_GETFL = 3
12
+ F_SETFL = 4
13
+ IGNPAR = 0x00000004
14
+ VMIN = 16
15
+ VTIME = 17
16
+ CLOCAL = 0x00008000
17
+ CREAD = 0x00000800
18
+ TCSANOW = 0
19
+ NCCS = 20
20
+
21
+ DATA_BITS = {
22
+ 5 => 0x00000000,
23
+ 6 => 0x00000100,
24
+ 7 => 0x00000200,
25
+ 8 => 0x00000300
26
+ }
27
+
28
+ BAUDE_RATES = {
29
+ 0 => 0,
30
+ 50 => 50,
31
+ 75 => 75,
32
+ 110 => 110,
33
+ 134 => 134,
34
+ 150 => 150,
35
+ 200 => 200,
36
+ 300 => 300,
37
+ 600 => 600,
38
+ 1200 => 1200,
39
+ 1800 => 1800,
40
+ 2400 => 2400,
41
+ 4800 => 4800,
42
+ 9600 => 9600,
43
+ 19200 => 19200,
44
+ 38400 => 38400,
45
+ 7200 => 7200,
46
+ 14400 => 14400,
47
+ 28800 => 28800,
48
+ 57600 => 57600,
49
+ 76800 => 76800,
50
+ 115200 => 115200,
51
+ 230400 => 230400
52
+ }
53
+
54
+ ERROR_CODES = {
55
+ 1 => "EPERM",
56
+ 2 => "ENOENT",
57
+ 3 => "ESRCH",
58
+ 4 => "EINTR",
59
+ 5 => "EIO",
60
+ 6 => "ENXIO",
61
+ 7 => "E2BIG",
62
+ 8 => "ENOEXEC",
63
+ 9 => "EBADF",
64
+ 10 => "ECHILD",
65
+ 11 => "EDEADLK",
66
+ 12 => "ENOMEM",
67
+ 13 => "EACCES",
68
+ 14 => "EFAULT",
69
+ 15 => "ENOTBLK",
70
+ 16 => "EBUSY",
71
+ 17 => "EEXIST",
72
+ 18 => "EXDEV",
73
+ 19 => "ENODEV",
74
+ 20 => "ENOTDIR",
75
+ 21 => "EISDIR",
76
+ 22 => "EINVAL",
77
+ 23 => "ENFILE",
78
+ 24 => "EMFILE",
79
+ 25 => "ENOTTY",
80
+ 26 => "ETXTBSY",
81
+ 27 => "EFBIG",
82
+ 28 => "ENOSPC",
83
+ 29 => "ESPIPE",
84
+ 30 => "EROFS",
85
+ 31 => "EMLINK",
86
+ 32 => "EPIPE",
87
+ 33 => "EDOM",
88
+ 34 => "ERANGE",
89
+ 35 => "EAGAIN",
90
+ 36 => "EINPROGRESS",
91
+ 37 => "EALREADY",
92
+ 38 => "ENOTSOCK",
93
+ 39 => "EDESTADDRREQ",
94
+ 40 => "EMSGSIZE",
95
+ 41 => "EPROTOTYPE",
96
+ 42 => "ENOPROTOOPT",
97
+ 43 => "EPROTONOSUPPORT",
98
+ 44 => "ESOCKTNOSUPPORT",
99
+ 45 => "ENOTSUP",
100
+ 46 => "EPFNOSUPPORT",
101
+ 47 => "EAFNOSUPPORT",
102
+ 48 => "EADDRINUSE",
103
+ 49 => "EADDRNOTAVAIL",
104
+ 50 => "ENETDOWN",
105
+ 51 => "ENETUNREACH",
106
+ 52 => "ENETRESET",
107
+ 53 => "ECONNABORTED",
108
+ 54 => "ECONNRESET",
109
+ 55 => "ENOBUFS",
110
+ 56 => "EISCONN",
111
+ 57 => "ENOTCONN",
112
+ 58 => "ESHUTDOWN",
113
+ 59 => "ETOOMANYREFS",
114
+ 60 => "ETIMEDOUT",
115
+ 61 => "ECONNREFUSED",
116
+ 62 => "ELOOP",
117
+ 63 => "ENAMETOOLONG",
118
+ 64 => "EHOSTDOWN",
119
+ 65 => "EHOSTUNREACH",
120
+ 66 => "ENOTEMPTY",
121
+ 67 => "EPROCLIM",
122
+ 68 => "EUSERS",
123
+ 69 => "EDQUOT",
124
+ 70 => "ESTALE",
125
+ 71 => "EREMOTE",
126
+ 72 => "EBADRPC",
127
+ 73 => "ERPCMISMATCH",
128
+ 74 => "EPROGUNAVAIL",
129
+ 75 => "EPROGMISMATCH",
130
+ 76 => "EPROCUNAVAIL",
131
+ 77 => "ENOLCK",
132
+ 78 => "ENOSYS",
133
+ 79 => "EFTYPE",
134
+ 80 => "EAUTH",
135
+ 81 => "ENEEDAUTH",
136
+ 82 => "EPWROFF",
137
+ 83 => "EDEVERR",
138
+ 84 => "EOVERFLOW",
139
+ 85 => "EBADEXEC",
140
+ 86 => "EBADARCH",
141
+ 87 => "ESHLIBVERS",
142
+ 88 => "EBADMACHO",
143
+ 89 => "ECANCELED",
144
+ 90 => "EIDRM",
145
+ 91 => "ENOMSG",
146
+ 92 => "EILSEQ",
147
+ 93 => "ENOATTR",
148
+ 94 => "EBADMSG",
149
+ 95 => "EMULTIHOP",
150
+ 96 => "ENODATA",
151
+ 97 => "ENOLINK",
152
+ 98 => "ENOSR",
153
+ 99 => "ENOSTR",
154
+ 100 => "EPROTO",
155
+ 101 => "ETIME",
156
+ 102 => "EOPNOTSUPP",
157
+ 103 => "ENOPOLICY",
158
+ 104 => "ENOTRECOVERABLE",
159
+ 105 => "EOWNERDEAD",
160
+ 106 => "ELAST"
161
+ }
162
+
163
+ class Termios < FFI::Struct
164
+ layout :c_iflag, :ulong,
165
+ :c_oflag, :ulong,
166
+ :c_cflag, :ulong,
167
+ :c_lflag, :ulong,
168
+ :c_line, :uchar,
169
+ :cc_c, [ :uchar, NCCS ],
170
+ :c_ispeed, :ulong,
171
+ :c_ospeed, :ulong
172
+ end
173
+
174
+ attach_function :tcsetattr, [ :int, :int, RubySerial::Posix::Termios ], :int
175
+ attach_function :fcntl, [:int, :int, :varargs], :int
176
+ attach_function :open, [:pointer, :int], :int
177
+ attach_function :close, [:int], :int
178
+ attach_function :write, [:int, :pointer, :int],:int
179
+ attach_function :read, [:int, :pointer, :int],:int
180
+ end
181
+ end
@@ -0,0 +1,84 @@
1
+ require 'ffi'
2
+
3
+ class Serial
4
+ def initialize(address, baude_rate=9600, data_bits=8)
5
+ file_opts = RubySerial::Posix::O_RDWR | RubySerial::Posix::O_NOCTTY
6
+ @fd = RubySerial::Posix.open(address, file_opts)
7
+
8
+ if @fd == -1
9
+ raise RubySerial::Exception, RubySerial::Posix::ERROR_CODES[FFI.errno]
10
+ else
11
+ @open = true
12
+ end
13
+
14
+ fl = RubySerial::Posix.fcntl(@fd, RubySerial::Posix::F_GETFL, :int, 0)
15
+ if fl == -1
16
+ raise RubySerial::Exception, RubySerial::Posix::ERROR_CODES[FFI.errno]
17
+ end
18
+
19
+ err = RubySerial::Posix.fcntl(@fd, RubySerial::Posix::F_SETFL, :int, ~RubySerial::Posix::O_NONBLOCK & fl)
20
+ if err == -1
21
+ raise RubySerial::Exception, RubySerial::Posix::ERROR_CODES[FFI.errno]
22
+ end
23
+
24
+ @config = build_config(baude_rate, data_bits)
25
+
26
+ err = RubySerial::Posix.tcsetattr(@fd, RubySerial::Posix::TCSANOW, @config)
27
+ if err == -1
28
+ raise RubySerial::Exception, RubySerial::Posix::ERROR_CODES[FFI.errno]
29
+ end
30
+ end
31
+
32
+ def closed?
33
+ !@open
34
+ end
35
+
36
+ def close
37
+ err = RubySerial::Posix.close(@fd)
38
+ if err == -1
39
+ raise RubySerial::Exception, RubySerial::Posix::ERROR_CODES[FFI.errno]
40
+ else
41
+ @open = false
42
+ end
43
+ end
44
+
45
+ def write(data)
46
+ n = 0
47
+ while data.size > n do
48
+ buff = FFI::MemoryPointer.from_string(data[n..-1].to_s)
49
+ i = RubySerial::Posix.write(@fd, buff, buff.size)
50
+ if i == -1
51
+ raise RubySerial::Exception, RubySerial::Posix::ERROR_CODES[FFI.errno]
52
+ else
53
+ n = n+i
54
+ end
55
+ end
56
+ end
57
+
58
+ def read(size)
59
+ buff = FFI::MemoryPointer.new :char, size
60
+ i = RubySerial::Posix.read(@fd, buff, size)
61
+ if i == -1
62
+ raise RubySerial::Exception, RubySerial::Posix::ERROR_CODES[FFI.errno]
63
+ end
64
+ buff.get_bytes(0, i)
65
+ end
66
+
67
+ private
68
+
69
+ def build_config(baude_rate, data_bits)
70
+ config = RubySerial::Posix::Termios.new
71
+
72
+ config[:c_iflag] = RubySerial::Posix::IGNPAR
73
+ config[:c_ispeed] = RubySerial::Posix::BAUDE_RATES[baude_rate]
74
+ config[:c_ospeed] = RubySerial::Posix::BAUDE_RATES[baude_rate]
75
+ config[:c_cflag] = RubySerial::Posix::DATA_BITS[data_bits] |
76
+ RubySerial::Posix::CREAD |
77
+ RubySerial::Posix::CLOCAL |
78
+ RubySerial::Posix::BAUDE_RATES[baude_rate]
79
+
80
+ config[:cc_c][RubySerial::Posix::VMIN] = 0
81
+
82
+ config
83
+ end
84
+ end
@@ -0,0 +1,5 @@
1
+ module RubySerial
2
+ unless const_defined?('VERSION')
3
+ VERSION = "0.0.1"
4
+ end
5
+ end
@@ -0,0 +1,74 @@
1
+ require 'ffi'
2
+
3
+ class Serial
4
+ def initialize(address, baude_rate=9600, data_bits=8)
5
+ file_opts = RubySerial::Win32::GENERIC_READ | RubySerial::Win32::GENERIC_WRITE
6
+ @fd = RubySerial::Win32.CreateFileA(address, file_opts, 0, nil, RubySerial::Win32::OPEN_EXISTING, 0, nil)
7
+ err = FFI.errno
8
+ if err != 0
9
+ raise RubySerial::Exception, RubySerial::Win32::ERROR_CODES[err]
10
+ else
11
+ @open = true
12
+ end
13
+
14
+ RubySerial::Win32::DCB.new.tap do |dcb|
15
+ dcb[:dcblength] = RubySerial::Win32::DCB::Sizeof
16
+ err = RubySerial::Win32.GetCommState @fd, dcb
17
+ if err == 0
18
+ raise RubySerial::Exception, RubySerial::Win32::ERROR_CODES[FFI.errno]
19
+ end
20
+ dcb[:baudrate] = baude_rate
21
+ dcb[:bytesize] = data_bits
22
+ dcb[:stopbits] = RubySerial::Win32::DCB::ONESTOPBIT
23
+ dcb[:parity] = RubySerial::Win32::DCB::NOPARITY
24
+ err = RubySerial::Win32.SetCommState @fd, dcb
25
+ if err == 0
26
+ raise RubySerial::Exception, RubySerial::Win32::ERROR_CODES[FFI.errno]
27
+ end
28
+ end
29
+
30
+ RubySerial::Win32::CommTimeouts.new.tap do |timeouts|
31
+ timeouts[:read_interval_timeout] = 50
32
+ timeouts[:read_total_timeout_multiplier] = 50
33
+ timeouts[:read_total_timeout_constant] = 10
34
+ timeouts[:write_total_timeout_multiplier] = 50
35
+ timeouts[:write_total_timeout_constant] = 10
36
+ err = RubySerial::Win32.SetCommTimeouts @fd, timeouts
37
+ if err == 0
38
+ raise RubySerial::Exception, RubySerial::Win32::ERROR_CODES[FFI.errno]
39
+ end
40
+ end
41
+ end
42
+
43
+ def read(size)
44
+ buff = FFI::MemoryPointer.new :char, size
45
+ count = FFI::MemoryPointer.new :uint32, 1
46
+ err = RubySerial::Win32.ReadFile(@fd, buff, size, count, nil)
47
+ if err == 0
48
+ raise RubySerial::Exception, RubySerial::Win32::ERROR_CODES[FFI.errno]
49
+ end
50
+ buff.get_bytes(0, count.read_string.unpack('H4').join().to_i(16))
51
+ end
52
+
53
+ def write(data)
54
+ buff = FFI::MemoryPointer.from_string(data.to_s)
55
+ count = FFI::MemoryPointer.new :uint32, 1
56
+ err = RubySerial::Win32.WriteFile(@fd, buff, buff.size, count, nil)
57
+ if err == 0
58
+ raise RubySerial::Exception, RubySerial::Win32::ERROR_CODES[FFI.errno]
59
+ end
60
+ end
61
+
62
+ def close
63
+ err = RubySerial::Win32.CloseHandle(@fd)
64
+ if err == 0
65
+ raise RubySerial::Exception, RubySerial::Win32::ERROR_CODES[FFI.errno]
66
+ else
67
+ @open = false
68
+ end
69
+ end
70
+
71
+ def closed?
72
+ !@open
73
+ end
74
+ end
@@ -0,0 +1,275 @@
1
+ require 'ffi'
2
+
3
+ module RubySerial
4
+ module Win32
5
+ extend FFI::Library
6
+ ffi_lib 'kernel32'
7
+ ffi_convention :stdcall
8
+
9
+ GENERIC_READ = 0x80000000
10
+ GENERIC_WRITE = 0x40000000
11
+ OPEN_EXISTING = 3
12
+
13
+ ERROR_CODES = {
14
+ 5 => "ERROR_ACCESS_DENIED",
15
+ 57 => "ERROR_ADAP_HDW_ERR",
16
+ 85 => "ERROR_ALREADY_ASSIGNED",
17
+ 183 => "ERROR_ALREADY_EXISTS",
18
+ 7 => "ERROR_ARENA_TRASHED",
19
+ 174 => "ERROR_ATOMIC_LOCKS_NOT_SUPPORTED",
20
+ 199 => "ERROR_AUTODATASEG_EXCEEDS_64k",
21
+ 160 => "ERROR_BAD_ARGUMENTS",
22
+ 22 => "ERROR_BAD_COMMAND",
23
+ 66 => "ERROR_BAD_DEV_TYPE",
24
+ 119 => "ERROR_BAD_DRIVER_LEVEL",
25
+ 10 => "ERROR_BAD_ENVIRONMENT",
26
+ 193 => "ERROR_BAD_EXE_FORMAT",
27
+ 222 => "ERROR_BAD_FILE_TYPE",
28
+ 11 => "ERROR_BAD_FORMAT",
29
+ 24 => "ERROR_BAD_LENGTH",
30
+ 67 => "ERROR_BAD_NET_NAME",
31
+ 58 => "ERROR_BAD_NET_RESP",
32
+ 53 => "ERROR_BAD_NETPATH",
33
+ 161 => "ERROR_BAD_PATHNAME",
34
+ 230 => "ERROR_BAD_PIPE",
35
+ 60 => "ERROR_BAD_REM_ADAP",
36
+ 159 => "ERROR_BAD_THREADID_ADDR",
37
+ 20 => "ERROR_BAD_UNIT",
38
+ 109 => "ERROR_BROKEN_PIPE",
39
+ 111 => "ERROR_BUFFER_OVERFLOW",
40
+ 142 => "ERROR_BUSY_DRIVE",
41
+ 170 => "ERROR_BUSY",
42
+ 120 => "ERROR_CALL_NOT_IMPLEMENTED",
43
+ 173 => "ERROR_CANCEL_VIOLATION",
44
+ 266 => "ERROR_CANNOT_COPY",
45
+ 82 => "ERROR_CANNOT_MAKE",
46
+ 221 => "ERROR_CHECKOUT_REQUIRED",
47
+ 129 => "ERROR_CHILD_NOT_COMPLETE",
48
+ 23 => "ERROR_CRC",
49
+ 16 => "ERROR_CURRENT_DIRECTORY",
50
+ 303 => "ERROR_DELETE_PENDING",
51
+ 55 => "ERROR_DEV_NOT_EXIST",
52
+ 145 => "ERROR_DIR_NOT_EMPTY",
53
+ 144 => "ERROR_DIR_NOT_ROOT",
54
+ 130 => "ERROR_DIRECT_ACCESS_HANDLE",
55
+ 267 => "ERROR_DIRECTORY",
56
+ 157 => "ERROR_DISCARDED",
57
+ 107 => "ERROR_DISK_CHANGE",
58
+ 112 => "ERROR_DISK_FULL",
59
+ 302 => "ERROR_DISK_TOO_FRAGMENTED",
60
+ 108 => "ERROR_DRIVE_LOCKED",
61
+ 52 => "ERROR_DUP_NAME",
62
+ 196 => "ERROR_DYNLINK_FROM_INVALID_RING",
63
+ 276 => "ERROR_EA_FILE_CORRUPT",
64
+ 255 => "ERROR_EA_LIST_INCONSISTENT",
65
+ 277 => "ERROR_EA_TABLE_FULL",
66
+ 275 => "ERROR_EAS_DIDNT_FIT",
67
+ 282 => "ERROR_EAS_NOT_SUPPORTED",
68
+ 203 => "ERROR_ENVVAR_NOT_FOUND",
69
+ 101 => "ERROR_EXCL_SEM_ALREADY_OWNED",
70
+ 217 => "ERROR_EXE_CANNOT_MODIFY_SIGNED_BINARY",
71
+ 218 => "ERROR_EXE_CANNOT_MODIFY_STRONG_SIGNED_BINARY",
72
+ 216 => "ERROR_EXE_MACHINE_TYPE_MISMATCH",
73
+ 192 => "ERROR_EXE_MARKED_INVALID",
74
+ 83 => "ERROR_FAIL_I24",
75
+ 350 => "ERROR_FAIL_NOACTION_REBOOT",
76
+ 352 => "ERROR_FAIL_RESTART",
77
+ 351 => "ERROR_FAIL_SHUTDOWN",
78
+ 220 => "ERROR_FILE_CHECKED_OUT",
79
+ 80 => "ERROR_FILE_EXISTS",
80
+ 2 => "ERROR_FILE_NOT_FOUND",
81
+ 223 => "ERROR_FILE_TOO_LARGE",
82
+ 206 => "ERROR_FILENAME_EXCED_RANGE",
83
+ 224 => "ERROR_FORMS_AUTH_REQUIRED",
84
+ 31 => "ERROR_GEN_FAILURE",
85
+ 39 => "ERROR_HANDLE_DISK_FULL",
86
+ 38 => "ERROR_HANDLE_EOF",
87
+ 308 => "ERROR_IMAGE_SUBSYSTEM_NOT_PRESENT",
88
+ 304 => "ERROR_INCOMPATIBLE_WITH_GLOBAL_SHORT_NAME_REGISTRY_SETTING",
89
+ 202 => "ERROR_INFLOOP_IN_RELOC_CHAIN",
90
+ 122 => "ERROR_INSUFFICIENT_BUFFER",
91
+ 12 => "ERROR_INVALID_ACCESS",
92
+ 487 => "ERROR_INVALID_ADDRESS",
93
+ 104 => "ERROR_INVALID_AT_INTERRUPT_TIME",
94
+ 9 => "ERROR_INVALID_BLOCK",
95
+ 117 => "ERROR_INVALID_CATEGORY",
96
+ 13 => "ERROR_INVALID_DATA",
97
+ 15 => "ERROR_INVALID_DRIVE",
98
+ 278 => "ERROR_INVALID_EA_HANDLE",
99
+ 254 => "ERROR_INVALID_EA_NAME",
100
+ 151 => "ERROR_INVALID_EVENT_COUNT",
101
+ 191 => "ERROR_INVALID_EXE_SIGNATURE",
102
+ 186 => "ERROR_INVALID_FLAG_NUMBER",
103
+ 1 => "ERROR_INVALID_FUNCTION",
104
+ 6 => "ERROR_INVALID_HANDLE",
105
+ 124 => "ERROR_INVALID_LEVEL",
106
+ 153 => "ERROR_INVALID_LIST_FORMAT",
107
+ 307 => "ERROR_INVALID_LOCK_RANGE",
108
+ 195 => "ERROR_INVALID_MINALLOCSIZE",
109
+ 190 => "ERROR_INVALID_MODULETYPE",
110
+ 123 => "ERROR_INVALID_NAME",
111
+ 301 => "ERROR_INVALID_OPLOCK_PROTOCOL",
112
+ 182 => "ERROR_INVALID_ORDINAL",
113
+ 87 => "ERROR_INVALID_PARAMETER",
114
+ 86 => "ERROR_INVALID_PASSWORD",
115
+ 198 => "ERROR_INVALID_SEGDPL",
116
+ 180 => "ERROR_INVALID_SEGMENT_NUMBER",
117
+ 209 => "ERROR_INVALID_SIGNAL_NUMBER",
118
+ 189 => "ERROR_INVALID_STACKSEG",
119
+ 188 => "ERROR_INVALID_STARTING_CODESEG",
120
+ 114 => "ERROR_INVALID_TARGET_HANDLE",
121
+ 118 => "ERROR_INVALID_VERIFY_SWITCH",
122
+ 197 => "ERROR_IOPL_NOT_ENABLED",
123
+ 147 => "ERROR_IS_JOIN_PATH",
124
+ 133 => "ERROR_IS_JOIN_TARGET",
125
+ 134 => "ERROR_IS_JOINED",
126
+ 146 => "ERROR_IS_SUBST_PATH",
127
+ 149 => "ERROR_IS_SUBST_TARGET",
128
+ 135 => "ERROR_IS_SUBSTED",
129
+ 194 => "ERROR_ITERATED_DATA_EXCEEDS_64k",
130
+ 138 => "ERROR_JOIN_TO_JOIN",
131
+ 140 => "ERROR_JOIN_TO_SUBST",
132
+ 154 => "ERROR_LABEL_TOO_LONG",
133
+ 167 => "ERROR_LOCK_FAILED",
134
+ 33 => "ERROR_LOCK_VIOLATION",
135
+ 212 => "ERROR_LOCKED",
136
+ 353 => "ERROR_MAX_SESSIONS_REACHED",
137
+ 164 => "ERROR_MAX_THRDS_REACHED",
138
+ 208 => "ERROR_META_EXPANSION_TOO_LONG",
139
+ 126 => "ERROR_MOD_NOT_FOUND",
140
+ 234 => "ERROR_MORE_DATA",
141
+ 317 => "ERROR_MR_MID_NOT_FOUND",
142
+ 131 => "ERROR_NEGATIVE_SEEK",
143
+ 215 => "ERROR_NESTING_NOT_ALLOWED",
144
+ 88 => "ERROR_NET_WRITE_FAULT",
145
+ 64 => "ERROR_NETNAME_DELETED",
146
+ 65 => "ERROR_NETWORK_ACCESS_DENIED",
147
+ 54 => "ERROR_NETWORK_BUSY",
148
+ 232 => "ERROR_NO_DATA",
149
+ 18 => "ERROR_NO_MORE_FILES",
150
+ 259 => "ERROR_NO_MORE_ITEMS",
151
+ 113 => "ERROR_NO_MORE_SEARCH_HANDLES",
152
+ 89 => "ERROR_NO_PROC_SLOTS",
153
+ 205 => "ERROR_NO_SIGNAL_SENT",
154
+ 62 => "ERROR_NO_SPOOL_SPACE",
155
+ 125 => "ERROR_NO_VOLUME_LABEL",
156
+ 26 => "ERROR_NOT_DOS_DISK",
157
+ 8 => "ERROR_NOT_ENOUGH_MEMORY",
158
+ 136 => "ERROR_NOT_JOINED",
159
+ 158 => "ERROR_NOT_LOCKED",
160
+ 288 => "ERROR_NOT_OWNER",
161
+ 21 => "ERROR_NOT_READY",
162
+ 17 => "ERROR_NOT_SAME_DEVICE",
163
+ 137 => "ERROR_NOT_SUBSTED",
164
+ 50 => "ERROR_NOT_SUPPORTED",
165
+ 309 => "ERROR_NOTIFICATION_GUID_ALREADY_DEFINED",
166
+ 110 => "ERROR_OPEN_FAILED",
167
+ 300 => "ERROR_OPLOCK_NOT_GRANTED",
168
+ 28 => "ERROR_OUT_OF_PAPER",
169
+ 84 => "ERROR_OUT_OF_STRUCTURES",
170
+ 14 => "ERROR_OUTOFMEMORY",
171
+ 299 => "ERROR_PARTIAL_COPY",
172
+ 148 => "ERROR_PATH_BUSY",
173
+ 3 => "ERROR_PATH_NOT_FOUND",
174
+ 231 => "ERROR_PIPE_BUSY",
175
+ 229 => "ERROR_PIPE_LOCAL",
176
+ 233 => "ERROR_PIPE_NOT_CONNECTED",
177
+ 63 => "ERROR_PRINT_CANCELLED",
178
+ 61 => "ERROR_PRINTQ_FULL",
179
+ 127 => "ERROR_PROC_NOT_FOUND",
180
+ 402 => "ERROR_PROCESS_MODE_ALREADY_BACKGROUND",
181
+ 403 => "ERROR_PROCESS_MODE_NOT_BACKGROUND",
182
+ 30 => "ERROR_READ_FAULT",
183
+ 72 => "ERROR_REDIR_PAUSED",
184
+ 201 => "ERROR_RELOC_CHAIN_XEEDS_SEGLIM",
185
+ 51 => "ERROR_REM_NOT_LIST",
186
+ 71 => "ERROR_REQ_NOT_ACCEP",
187
+ 207 => "ERROR_RING2_STACK_IN_USE",
188
+ 200 => "ERROR_RING2SEG_MUST_BE_MOVABLE",
189
+ 143 => "ERROR_SAME_DRIVE",
190
+ 318 => "ERROR_SCOPE_NOT_FOUND",
191
+ 27 => "ERROR_SECTOR_NOT_FOUND",
192
+ 306 => "ERROR_SECURITY_STREAM_IS_INCONSISTENT",
193
+ 132 => "ERROR_SEEK_ON_DEVICE",
194
+ 25 => "ERROR_SEEK",
195
+ 102 => "ERROR_SEM_IS_SET",
196
+ 187 => "ERROR_SEM_NOT_FOUND",
197
+ 105 => "ERROR_SEM_OWNER_DIED",
198
+ 121 => "ERROR_SEM_TIMEOUT",
199
+ 106 => "ERROR_SEM_USER_LIMIT",
200
+ 36 => "ERROR_SHARING_BUFFER_EXCEEDED",
201
+ 70 => "ERROR_SHARING_PAUSED",
202
+ 32 => "ERROR_SHARING_VIOLATION",
203
+ 305 => "ERROR_SHORT_NAMES_NOT_ENABLED_ON_VOLUME",
204
+ 162 => "ERROR_SIGNAL_PENDING",
205
+ 156 => "ERROR_SIGNAL_REFUSED",
206
+ 141 => "ERROR_SUBST_TO_JOIN",
207
+ 139 => "ERROR_SUBST_TO_SUBST",
208
+ 0 => "ERROR_SUCCESS",
209
+ 150 => "ERROR_SYSTEM_TRACE",
210
+ 210 => "ERROR_THREAD_1_INACTIVE",
211
+ 400 => "ERROR_THREAD_MODE_ALREADY_BACKGROUND",
212
+ 401 => "ERROR_THREAD_MODE_NOT_BACKGROUND",
213
+ 56 => "ERROR_TOO_MANY_CMDS",
214
+ 214 => "ERROR_TOO_MANY_MODULES",
215
+ 152 => "ERROR_TOO_MANY_MUXWAITERS",
216
+ 68 => "ERROR_TOO_MANY_NAMES",
217
+ 4 => "ERROR_TOO_MANY_OPEN_FILES",
218
+ 298 => "ERROR_TOO_MANY_POSTS",
219
+ 103 => "ERROR_TOO_MANY_SEM_REQUESTS",
220
+ 100 => "ERROR_TOO_MANY_SEMAPHORES",
221
+ 69 => "ERROR_TOO_MANY_SESS",
222
+ 155 => "ERROR_TOO_MANY_TCBS",
223
+ 59 => "ERROR_UNEXP_NET_ERR",
224
+ 240 => "ERROR_VC_DISCONNECTED",
225
+ 226 => "ERROR_VIRUS_DELETED",
226
+ 225 => "ERROR_VIRUS_INFECTED",
227
+ 128 => "ERROR_WAIT_NO_CHILDREN",
228
+ 29 => "ERROR_WRITE_FAULT",
229
+ 19 => "ERROR_WRITE_PROTECT",
230
+ 34 => "ERROR_WRONG_DISK",
231
+ 258 => "WAIT_TIMEOUT"
232
+ }
233
+
234
+ class DCB < FFI::Struct
235
+ layout :dcblength, :uint32,
236
+ :baudrate, :uint32,
237
+ :flags, :uint32, # :flag is actually a bit fields compound:
238
+ :wreserved, :uint16, # uint32 fBinary :1;
239
+ :xonlim, :uint16, # uint32 fParity :1;
240
+ :xofflim, :uint16, # uint32 fParity :1;
241
+ :bytesize, :uint8, # uint32 fOutxCtsFlow :1;
242
+ :parity, :uint8, # uint32 fOutxDsrFlow :1;
243
+ :stopbits, :uint8, # uint32 fDtrControl :2;
244
+ :xonchar, :int8, # uint32 fDsrSensitivity :1;
245
+ :xoffchar, :int8, # uint32 fTXContinueOnXoff :1;
246
+ :errorchar, :int8, # uint32 fOutX :1;
247
+ :eofchar, :int8, # uint32 fInX :1;
248
+ :evtchar, :int8, # uint32 fErrorChar :1;
249
+ :wreserved1, :uint16 # uint32 fNull :1;
250
+ # uint32 fRtsControl :2;
251
+ # uint32 fAbortOnError :1;
252
+ # uint32 fDummy2 :17;
253
+ Sizeof = 28
254
+ ONESTOPBIT = 0
255
+ NOPARITY = 0
256
+ end
257
+
258
+ class CommTimeouts < FFI::Struct
259
+ layout :read_interval_timeout, :uint32,
260
+ :read_total_timeout_multiplier, :uint32,
261
+ :read_total_timeout_constant, :uint32,
262
+ :write_total_timeout_multiplier, :uint32,
263
+ :write_total_timeout_constant, :uint32
264
+ end
265
+
266
+ attach_function :CreateFileA, [:pointer, :uint32, :uint32, :pointer, :uint32, :uint32, :pointer], :pointer
267
+ attach_function :CloseHandle, [:pointer], :int
268
+ attach_function :ReadFile, [:pointer, :pointer, :uint32, :pointer, :pointer], :int32
269
+ attach_function :WriteFile, [:pointer, :pointer, :uint32, :pointer, :pointer], :int32
270
+ attach_function :GetCommState, [:pointer, RubySerial::Win32::DCB], :int32
271
+ attach_function :SetCommState, [:pointer, RubySerial::Win32::DCB], :int32
272
+ attach_function :GetCommTimeouts, [:pointer, RubySerial::Win32::CommTimeouts], :int32
273
+ attach_function :SetCommTimeouts, [:pointer, RubySerial::Win32::CommTimeouts], :int32
274
+ end
275
+ end
@@ -0,0 +1,19 @@
1
+ $:.push File.expand_path("../lib", __FILE__)
2
+ require "rubyserial/version"
3
+
4
+ Gem::Specification.new do |s|
5
+ s.name = "rubyserial"
6
+ s.version = RubySerial::VERSION
7
+ s.summary = "ffi ruby serialport gem"
8
+ s.description = "ffi ruby serialport gem"
9
+ s.homepage = "https://github.com/hybridgroup/rubyserial"
10
+ s.authors = ["Adrian Zankich", "Theron Boerner", "Javier Cervantes"]
11
+ s.platform = Gem::Platform::RUBY
12
+ s.license = 'Apache 2.0'
13
+
14
+ s.files = `git ls-files`.split("\n")
15
+ s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
16
+ s.require_paths = ["lib"]
17
+
18
+ s.add_runtime_dependency 'ffi', '~> 1.9.3'
19
+ end
metadata ADDED
@@ -0,0 +1,72 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: rubyserial
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1
5
+ platform: ruby
6
+ authors:
7
+ - Adrian Zankich
8
+ - Theron Boerner
9
+ - Javier Cervantes
10
+ autorequire:
11
+ bindir: bin
12
+ cert_chain: []
13
+ date: 2014-07-01 00:00:00.000000000 Z
14
+ dependencies:
15
+ - !ruby/object:Gem::Dependency
16
+ name: ffi
17
+ requirement: !ruby/object:Gem::Requirement
18
+ requirements:
19
+ - - "~>"
20
+ - !ruby/object:Gem::Version
21
+ version: 1.9.3
22
+ type: :runtime
23
+ prerelease: false
24
+ version_requirements: !ruby/object:Gem::Requirement
25
+ requirements:
26
+ - - "~>"
27
+ - !ruby/object:Gem::Version
28
+ version: 1.9.3
29
+ description: ffi ruby serialport gem
30
+ email:
31
+ executables: []
32
+ extensions: []
33
+ extra_rdoc_files: []
34
+ files:
35
+ - ".gitignore"
36
+ - Gemfile
37
+ - LICENSE
38
+ - README.md
39
+ - Rakefile
40
+ - lib/rubyserial.rb
41
+ - lib/rubyserial/linux_constants.rb
42
+ - lib/rubyserial/osx_constants.rb
43
+ - lib/rubyserial/posix.rb
44
+ - lib/rubyserial/version.rb
45
+ - lib/rubyserial/windows.rb
46
+ - lib/rubyserial/windows_constants.rb
47
+ - rubyserial.gemspec
48
+ homepage: https://github.com/hybridgroup/rubyserial
49
+ licenses:
50
+ - Apache 2.0
51
+ metadata: {}
52
+ post_install_message:
53
+ rdoc_options: []
54
+ require_paths:
55
+ - lib
56
+ required_ruby_version: !ruby/object:Gem::Requirement
57
+ requirements:
58
+ - - ">="
59
+ - !ruby/object:Gem::Version
60
+ version: '0'
61
+ required_rubygems_version: !ruby/object:Gem::Requirement
62
+ requirements:
63
+ - - ">="
64
+ - !ruby/object:Gem::Version
65
+ version: '0'
66
+ requirements: []
67
+ rubyforge_project:
68
+ rubygems_version: 2.2.2
69
+ signing_key:
70
+ specification_version: 4
71
+ summary: ffi ruby serialport gem
72
+ test_files: []