rubyserial 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -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: []