/
lsport.go
267 lines (236 loc) · 8.13 KB
/
lsport.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
// Package lsport is a go wrapper for the cross-platform C library libserialport.
//
// Libserialport is a fairly thorough library and lsport only implements a subset
// of the full functionality available, it should however be fairly straightforward
// to add the remainder should you wish to do so.
// Documentation for libserialport can be found at http://sigrok.org/wiki/Libserialport
//
// Installation
//
// Using go get is (I think) impossible at the moment, at least in a cross platform
// way, as libserialport must be configured on the target system. So instead you
// should proceed as follows
// git clone http://github.com/kezl/lsport
// into the src directory of your GOPATH. You'll then need to alter the #cgo CFLAGS
// & #cgo LDFLAGS in lsport.go so they are absolute (automation of absolute paths
// is coming to go1.5 as ${SRCDIR}) but for now edit /your/actual/gopath/ to whatever
// it is:
// #cgo CFLAGS: -I/your/actual/gopath/src/github.com/kezl/lsport/libserialport/
// #cgo LDFLAGS: /your/actual/gopath/src/github.com/kezl/lsport/libserialport/.libs/libserialport.a
// and then build libserialport with something like:
// cd libserialport
// ./autogen.sh
// ./configure
// make
// before installing lsport with:
// go install github.com/kezl/lsport
package lsport
/*
// Use absolute paths here, relative paths are coming to go1.5 as ${SRCDIR} ?
#cgo CFLAGS: -I/your/actual/gopath/src/github.com/kezl/lsport/libserialport/
#cgo LDFLAGS: /your/actual/gopath/src/github.com/kezl/lsport/libserialport/.libs/libserialport.a
// define _GNU_SOURCE for asprintf
#define _GNU_SOURCE
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <libserialport.h>
static int append(char **str, const char *buf, int size) {
char *nstr;
if (*str == NULL) {
nstr = malloc(size + 1);
memcpy(nstr, buf, size);
nstr[size] = '\0';
}
else {
if (asprintf(&nstr, "%s%.*s", *str, size, buf) == -1) return -1;
free(*str);
}
*str = nstr;
return 0;
}
char *listPorts() {
char *str = NULL;
struct sp_port **ports = NULL;
struct sp_port *port = NULL;
int result = sp_list_ports(&ports);
int i = 0;
while (ports[i] != NULL) {
port = ports[i];
char* name = sp_get_port_name(port);
append(&str, name, strlen(name));
append(&str, "#", 1);
i++;
}
return str;
}
*/
import "C"
// NB no blank lines between */ and import "C" ;-)
import (
"errors"
"strings"
"unsafe"
)
// Return values for sp_port enums for errors and configurations
const (
/** Operation completed successfully. */
SP_OK = 0
/** Invalid arguments were passed to the function. */
SP_ERR_ARG = -1
/** A system error occured while executing the operation. */
SP_ERR_FAIL = -2
/** A memory allocation failed while executing the operation. */
SP_ERR_MEM = -3
/** The requested operation is not supported by this system or device. */
SP_ERR_SUPP = -4
/** Special value to indicate setting should be left alone. */
SP_PARITY_INVALID = -1
/** No parity. */
SP_PARITY_NONE = 0
/** Odd parity. */
SP_PARITY_ODD = 1
/** Even parity. */
SP_PARITY_EVEN = 2
/** Mark parity. */
SP_PARITY_MARK = 3
/** Space parity. */
SP_PARITY_SPACE = 4
/** Open port for read access. */
SP_MODE_READ = 1
/** Open port for write access. */
SP_MODE_WRITE = 2
/** Open port for read and write access. */
SP_MODE_READ_WRITE = 3
/** Input buffer. */
SP_BUF_INPUT = 1
/** Output buffer. */
SP_BUF_OUTPUT = 2
/** Both buffers. */
SP_BUF_BOTH = 3
)
// Port a pointer to an sp_port structure containing the file handle and
// port descriptors.
type Port *C.struct_sp_port
// Conf a struct containing a *C.struct_sp_port Port, the old port configuration
// and the active port configuration (both of type *C.struct_sp_port_config).
type Conf struct {
Port *C.struct_sp_port
oldConfig *C.struct_sp_port_config
newConfig *C.struct_sp_port_config
}
// checkResult error messages for sp_port return values.
func checkResult(result int32) error {
if result == SP_OK {
// Operation completed successfully.
return nil
} else if result == SP_ERR_ARG {
return errors.New("Invalid arguments were passed to the function.\n")
} else if result == SP_ERR_FAIL {
return errors.New("A system error occured while executing the operation.\n")
} else if result == SP_ERR_MEM {
return errors.New("A memory allocation failed while executing the operation.\n")
} else if result == SP_ERR_SUPP {
return errors.New("The requested operation is not supported by this system or device.\n")
}
return nil
}
// Init attempts to initialises the port represented by the port name passed in.
func Init(s *Conf, name string) (int32, error) {
var result int32 = SP_OK
C.sp_new_config(&s.oldConfig)
C.sp_new_config(&s.newConfig)
cp := C.CString(name)
defer C.free(unsafe.Pointer(cp))
result = C.sp_get_port_by_name(cp, &s.Port)
checkResult(result)
// Open before setting params
result = C.sp_open(s.Port, SP_MODE_READ|SP_MODE_WRITE)
return result, checkResult(result)
}
// SetParams sets the common port options: baudrate, bits and stopbits.
func SetParams(s *Conf, baud int, bits int, stopbits int) (int32, error) {
var result int32 = SP_OK
C.sp_set_config_baudrate(s.newConfig, C.int(baud))
C.sp_set_config_bits(s.newConfig, C.int(bits))
C.sp_set_config_parity(s.newConfig, SP_PARITY_NONE)
C.sp_set_config_stopbits(s.newConfig, C.int(stopbits))
result = C.sp_set_config(s.Port, s.newConfig)
return result, checkResult(result)
}
// Close restores the port to how it was before initialisation, closes the port and
// frees resources.
func Close(s *Conf) {
C.sp_flush(s.Port, SP_BUF_BOTH)
C.sp_free_config(s.newConfig)
C.sp_set_config(s.Port, s.oldConfig)
C.sp_free_config(s.oldConfig)
C.sp_close(s.Port)
}
// PortSlice returns a string slice of serial ports found on the system.
func PortsSlice() ([]string, error) {
portsStr := C.GoString(C.listPorts())
ports := strings.Split(strings.Trim(portsStr, "#"), "#")
if len(ports) == 0 {
return ports, errors.New("No serial ports available on the system.\n")
}
return ports, nil
}
// minInt32 returns the lesser of two int32s.
func minInt32(a, b int32) int32 {
if a < b {
return a
}
return b
}
// Waiting returns the number of bytes waiting on success or an error code.
func Waiting(port Port) (int32, error) {
var result int32 = SP_OK
result = C.sp_input_waiting(port)
return result, checkResult(result)
}
// BlockingRead attempts to read the lesser of the number of bytes waiting or the
// capacity of the rx buffer, blocks while reading, timeout is the number of mS
// to wait or set 0 to wait indefinitely.
// rxBuf size should not exceed the capacity of int32
func BlockingRead(port Port, rxBuf []byte, timeout uint) (int32, error) {
var result int32 = SP_OK
waiting, err := Waiting(port)
if err == nil {
// Passing to C so don't exceed the buffer length
length := minInt32(waiting, int32(len(rxBuf)))
if waiting > 0 {
result = C.sp_blocking_read(port, (unsafe.Pointer(&rxBuf[0])), C.size_t(length), C.uint(timeout))
}
return result, checkResult(result)
}
return waiting, checkResult(waiting)
}
// BlockingWrite attempts to write the string or buffer supplied to the port
// blocks while writing, timeout is the number of mS to wait or set 0 to wait
// indefinitely.
func BlockingWrite(port Port, txBuf []byte, timeout uint16) (int32, error) {
var result int32 = SP_OK
result = C.sp_blocking_write(port, (unsafe.Pointer(&txBuf[0])), C.size_t(len(txBuf)), C.uint(timeout))
return result, checkResult(result)
}
// Read as BlockingRead but non-blocking and no timeout .
func Read(port Port, rxBuf []byte) (int32, error) {
var result int32 = SP_OK
waiting, err := Waiting(port)
if err == nil {
// Passing to C so don't exceed the buffer length
length := minInt32(waiting, int32(len(rxBuf)))
if waiting > 0 {
result = C.sp_nonblocking_read(port, (unsafe.Pointer(&rxBuf[0])), C.size_t(length))
}
return result, checkResult(result)
}
return waiting, checkResult(waiting)
}
// Write as BlockingWrite but non-blocking and no timeout.
func Write(port Port, txBuf []byte) (int32, error) {
var result int32 = SP_OK
result = C.sp_nonblocking_write(port, (unsafe.Pointer(&txBuf[0])), C.size_t(len(txBuf)))
return result, checkResult(result)
}