forked from BenW0/IMC_Closed_Loop
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathrawhid_msg.c
243 lines (220 loc) · 8.06 KB
/
rawhid_msg.c
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
/********************************************************************************
* RawHID Messaging Module
* Ben Weiss, University of Washington 2014
* Purpose: Handles the RawHID interface for sending and receiving commands from
* my desktop-based rawhid_listener program. Basically, data is sent out in one
* of two forms: Debug and text stream data is written using hid_printf
* or hid_print, which is split into packet-sized pieces, given a header, and
* sent out over the rawhid interface. Binary packet data can be sent using
* hid_write or hid_write_packet. hid_write breaks the data into packets and
* appends a header; hid_write_packet just writes the raw packet data to the rawhid
* interface.
*
* All outgoing packets contain a header, the first two bits of which are a packet type
* identifier (0b00 is text, 0b01-0b11 are user-specified data channels), followed by 6 bits
* indicating the packet length (0-63). A special packet header, 0b11111100 (=text packet,
* length 64) is a special packet used to retrieve the device ID.
*
* Received packets can be read through hid_read(), which filters for a special
* header requesting a device ID, used to distinguish between multiple Teensy's connected
* to the same computer and running the same code. Except for that special header (0x08 =
* backspace), incoming packets are not expected to have a header.
*
* IF the device is configured to operate as a serial device (-DUSB_SERIAL instead of
* -DUSB_RAWHID the Makefile), this module sends all text to the host over serial without a header.
* Data streams each get a header including length information as before. This feature
* has not been tested.
*
*
* Source:
*
* License:
* This software is (c) 2014 by Ben Weiss and is released under the following license:
* The MIT License (MIT)
*
* Copyright (c) 2014 Ben Weiss
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
********************************************************************************/
#include <stdint.h>
#include <stdbool.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
// Note: This module does not include common.h. This is supposed to be a standalone resource...
#ifdef USB_RAWHID
#include <usb_rawhid.h>
#include <usb_seremu.h>
#else
#include <usb_serial.h>
#endif
#include <usb_desc.h>
#include "rawhid_msg.h"
#include "imc/utils.h" // for vmemset
// Constants ===========================================================
// Local Variables =====================================================
char message[200];
uint8_t pack[HID_PACKLEN];
uint32_t pack_len = 0;
uint32_t devid = 0; // device ID
// this function sets up the rawhid module. Providing a device_id allows the host computer
// to differentiate between teensys running the same code connected at the same time.
void hid_init(uint32_t device_id)
{
devid = device_id;
vmemset(pack, 0, HID_PACKLEN);
pack_len = 0;
}
// This code is not terribly robust; it may block while waiting for a packet to
// buffer for up to 100 ms, and it doesn't know how to recover from lost packets
void hid_printf(const char *str, ...)
{
va_list args;
va_start(args, str);
vsprintf(message, str, args);
hid_print(message, strlen(message), 100);
va_end(args);
}
// pack_type specifies the data stream to write on. If this function
// is called successively with the same packet type, data is collapsed into as few
// packets as possible. To ensure packets have all been sent, use hid_flush();
// Returns 1 if successful, 0 if unable to send before timeout. A 0 return may
// still have sent or cached some of the packet data to be sent in a future packet.
int hid_write(hid_pack_type pack_type, const uint8_t *data, uint32_t count, uint32_t timeout)
{
#ifndef USB_RAWHID
// shortcut for text streams -- just fire them off
if(TX_PACK_TYPE_TEXT == pack_type)
{
usb_serial_write(data, count);
return 1;
}
#endif
// check -- is there data waiting to be written?
if(pack_len)
{
// does the data waiting have the same header?
if(pack_type != (pack[0] & TX_PACK_TYPE_MASK))
{
// fail if we couldn't flush the buffered packet!
if(!hid_flush(timeout))
return 0;
pack[0] = pack_type;
pack_len = 1;
}
}
else
{
pack[0] = pack_type;
pack_len = 1;
}
// append the data to the packet(s)
for(uint32_t i = 0; i < count; i++)
{
pack[pack_len++] = data[i];
if(HID_PACKLEN == pack_len) // packet full!
{
// complete by inserting the payload length into the packet header
pack[0] |= (HID_PACKLEN - 1) << 2;
if(!hid_write_packet((void *)pack, timeout))
return 0;
pack_len = 1;
pack[0] = pack_type;
}
}
// if we ended on an even packet, reset the pack buffer. Otherwise we'll send an empty
// packet if the next call has a different header.
if(1 == pack_len)
pack_len = 0;
return 1;
}
// data contains the packet to send; timeout is the number of ms to wait for
// space on the tx queue before failing (--> return 0). Don't use this to directly
// send packets as a suer. Use hid_print or hid_write instead.
__attribute__ ((always_inline)) inline int hid_write_packet(const void *data, uint32_t timeout)
{
#ifdef USB_RAWHID
return usb_rawhid_send(data, timeout);
#else
return usb_serial_write(data, HID_PACKLEN);
#endif
}
// flushes the local not-full packet if one is waiting to be sent.
int hid_flush(uint32_t timeout)
{
if(!pack_len)
return 1; // nothing to send
// send a partially-full packet.
vmemset(pack + pack_len, 0, HID_PACKLEN - pack_len);
// complete the header by adding the packet length
pack[0] |= (pack_len - 1) << 2;
#ifdef USB_RAWHID
if(usb_rawhid_send(pack, timeout))
#else
if(usb_serial_write(pack, pack_len))
#endif
{
pack_len = 0;
return 1;
}
else
return 0; // keep the packet on the buffer.
}
// buf should be a buffer to write to, 64 bytes long. Timeout is the time to wait
// (in ms) before failing the read. If the read times out (no data available), 0 is returned,
// if it succeeds, the number of bytes read (always 64) is returned. Even if the
// read fails, buf may be overwritten!
int hid_read(uint8_t *buf, uint32_t timeout)
{
#ifdef USB_RAWHID
if(!usb_rawhid_recv((void*)buf, timeout))
return 0;
#else
if(!usb_serial_read((void*)buf, HID_PACKLEN))
return 0;
#endif
// check for device id request packet. The header is repeated 3x just to make sure this
// isn't a normal packet...
while(RX_HEAD_DEVID == buf[0] && RX_HEAD_DEVID == buf[1] && RX_HEAD_DEVID == buf[2])
{
// write back our device ID:
buf[0] = TX_HEAD_DEVID;
for(uint8_t i = 0; i < 3; i++)
buf[i+1] = (uint8_t)((devid >> (i*8)) & 0xFF);
hid_write_packet(buf, timeout); // if the buffer's full, oh well.
// read another packet
#ifdef USB_RAWHID
if(!usb_rawhid_recv((void*)buf, timeout))
return 0;
#else
if(!usb_serial_read((void*)buf, HID_PACKLEN))
return 0;
#endif
}
// this packet can be returned
return 1;
}
int hid_available(void)
{
#ifdef USB_RAWHID
return usb_rawhid_available();
#else
return usb_serial_available();
#endif
}