-
Notifications
You must be signed in to change notification settings - Fork 5
/
device.go
152 lines (139 loc) · 4.16 KB
/
device.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
// Copyright (c) 2015-2024 The usbtmc developers. All rights reserved.
// Project site: https://github.com/gotmc/usbtmc
// Use of this source code is governed by a MIT-style license that
// can be found in the LICENSE.txt file for the project.
package usbtmc
import (
"bytes"
"encoding/binary"
"fmt"
"io"
"strings"
"github.com/gotmc/usbtmc/driver"
)
// Device models a USBTMC device, which includes a USB device and the required
// USBTMC attributes and methods.
type Device struct {
usbDevice driver.USBDevice
bTag byte
termChar byte
termCharEnabled bool
}
// Write creates the appropriate USBMTC header, writes the header and data on
// the bulk out endpoint, and returns the number of bytes written and any
// errors.
func (d *Device) Write(p []byte) (n int, err error) {
// FIXME(mdr): I need to change this so that I look at the size of the buf
// being written to see if it can truly fit into one transfer, and if not
// split it into multiple transfers.
maxTransferSize := 512
for pos := 0; pos < len(p); {
d.bTag = nextbTag(d.bTag)
thisLen := len(p[pos:])
if thisLen > maxTransferSize-bulkOutHeaderSize {
thisLen = maxTransferSize - bulkOutHeaderSize
}
header := encodeBulkOutHeader(d.bTag, uint32(thisLen), true)
data := append(header[:], p[pos:pos+thisLen]...)
if moduloFour := len(data) % 4; moduloFour > 0 {
numAlignment := 4 - moduloFour
alignment := bytes.Repeat([]byte{0x00}, numAlignment)
data = append(data, alignment...)
}
_, err := d.usbDevice.Write(data)
if err != nil {
return pos, err
}
pos += thisLen
}
return len(p), nil
}
// Read creates and sends the header on the bulk out endpoint and then reads
// from the bulk in endpoint per USBTMC standard.
func (d *Device) Read(p []byte) (n int, err error) {
d.bTag = nextbTag(d.bTag)
header := encodeMsgInBulkOutHeader(d.bTag, uint32(len(p)), d.termCharEnabled, d.termChar)
if _, err = d.usbDevice.Write(header[:]); err != nil {
return 0, err
}
pos := 0
var transfer int
for pos < len(p) {
var resp int
var err error
if pos == 0 {
resp, transfer, err = d.readRemoveHeader(p[pos:])
} else {
resp, err = d.readKeepHeader(p[pos:])
}
if err != nil {
return pos, err
}
if resp == 0 {
break
}
pos += resp
if pos >= transfer {
break
}
}
return pos, nil
}
func (d *Device) readRemoveHeader(p []byte) (n int, transfer int, err error) {
// FIXME(mdr): Seems like I shouldn't use 512 as a magic number or as a hard
// size limit. I should grab the max size of the bulk in endpoint.
usbtmcHeaderLen := 12
temp := make([]byte, 512)
n, err = d.usbDevice.Read(temp)
// Remove the USBMTC Bulk-IN Header from the data and the number of bytes
if n < usbtmcHeaderLen {
return 0, 0, err
}
t32 := binary.LittleEndian.Uint32(temp[4:8])
transfer = int(t32)
reader := bytes.NewReader(temp)
_, err = reader.ReadAt(p, int64(usbtmcHeaderLen))
if err != nil && err != io.EOF {
return n - usbtmcHeaderLen, transfer, err
}
return n - usbtmcHeaderLen, transfer, nil
}
func (d *Device) readKeepHeader(p []byte) (n int, err error) {
return d.usbDevice.Read(p)
}
// Close closes the underlying USB device.
func (d *Device) Close() error {
return d.usbDevice.Close()
}
// WriteString writes a string using the underlying USB device. A newline
// terminator is not automatically added.
func (d *Device) WriteString(s string) (n int, err error) {
return d.Write([]byte(s))
}
// Command sends the SCPI/ASCII command to the underlying USB device. A newline
// character is automatically added to the end of the string.
func (d *Device) Command(format string, a ...interface{}) error {
cmd := format
if a != nil {
cmd = fmt.Sprintf(format, a...)
}
_, err := d.WriteString(strings.TrimSpace(cmd) + "\n")
return err
}
// Query writes the given string to the USBTMC device and returns the returned
// value as a string. A newline character is automatically added to the query
// command sent to the instrument.
func (d *Device) Query(s string) (string, error) {
err := d.Command(s)
if err != nil {
return "", err
}
p := make([]byte, 512)
n, err := d.Read(p)
if err != nil {
return "", err
}
s = fmt.Sprintf("%s", p[:n])
p = nil
return s, nil
}