11
11
//! 5. Call `usb.task()` within the main loop
12
12
13
13
const std = @import ("std" );
14
+ const builtin = @import ("builtin" );
14
15
15
16
/// USB Human Interface Device (HID)
16
17
pub const hid = @import ("usb/hid.zig" );
@@ -84,7 +85,28 @@ pub fn Usb(comptime f: anytype) type {
84
85
var started = false ;
85
86
// Some scratch space that we'll use for things like preparing string
86
87
// descriptors for transmission.
87
- var tmp : [64 ]u8 = .{0 } ** 64 ;
88
+ var tmp : [128 ]u8 = .{0 } ** 128 ;
89
+ // Keeps track of sent data from tmp buffer
90
+ var buffer_reader = BufferReader { .buffer = &.{} };
91
+ };
92
+
93
+ // Command endpoint utilities
94
+ const CmdEndpoint = struct {
95
+
96
+ /// Command response utility function that can split long data in multiple packets
97
+ fn send_cmd_response (data : []const u8 , expected_max_length : u16 ) void {
98
+ const cmd_in_endpoint = usb_config .? .endpoints [EP0_IN_IDX ];
99
+
100
+ S .buffer_reader = BufferReader { .buffer = data [0.. @min (data .len , expected_max_length )] };
101
+ const data_chunk = S .buffer_reader .try_peek (cmd_in_endpoint .descriptor .max_packet_size );
102
+
103
+ if (data_chunk .len > 0 ) {
104
+ f .usb_start_tx (
105
+ cmd_in_endpoint ,
106
+ data_chunk
107
+ );
108
+ }
109
+ }
88
110
};
89
111
90
112
// Check which interrupt flags are set.
@@ -156,69 +178,36 @@ pub fn Usb(comptime f: anytype) type {
156
178
// implementation.
157
179
usb_config .? .endpoints [EP0_IN_IDX ].next_pid_1 = true ;
158
180
159
- const dc = usb_config .? . device_descriptor . serialize () ;
160
- @memcpy ( S . tmp [0 .. dc . len ], & dc );
181
+ var bw = BufferWriter { . buffer = & S . tmp } ;
182
+ try bw . write ( & usb_config .? . device_descriptor . serialize () );
161
183
162
- // Configure EP0 IN to send the device descriptor
163
- // when it's next asked.
164
- f .usb_start_tx (
165
- usb_config .? .endpoints [EP0_IN_IDX ],
166
- S .tmp [0.. dc .len ],
167
- );
184
+ CmdEndpoint .send_cmd_response (bw .get_written_slice (), setup .length );
168
185
},
169
186
.Config = > {
170
187
if (debug ) std .log .info (" Config" , .{});
188
+
171
189
// Config descriptor requests are slightly unusual.
172
190
// We can respond with just our config descriptor,
173
191
// but we can _also_ append our interface and
174
192
// endpoint descriptors to the end, saving some
175
- // round trips. We'll choose to do this if the
176
- // number of bytes the host will accept (in the
177
- // `length` field) is large enough.
178
- var used : usize = 0 ;
179
-
180
- const cd = usb_config .? .config_descriptor .serialize ();
181
- @memcpy (S .tmp [used .. used + cd .len ], & cd );
182
- used += cd .len ;
183
-
184
- if (setup .length > used ) {
185
- // Do the rest!
186
- //
187
- // This is slightly incorrect because the host
188
- // might have asked for a number of bytes in
189
- // between the size of a config descriptor, and
190
- // the amount we're going to send back. However,
191
- // in practice, the host always asks for either
192
- // (1) the exact size of a config descriptor, or
193
- // (2) 64 bytes, and this all fits in 64 bytes.
194
- const id = usb_config .? .interface_descriptor .serialize ();
195
- @memcpy (S .tmp [used .. used + id .len ], & id );
196
- used += id .len ;
197
-
198
- // Seems like the host does not bother asking for the
199
- // hid descriptor so we'll just send it with the
200
- // other descriptors.
201
- if (usb_config .? .hid ) | hid_conf | {
202
- const hd = hid_conf .hid_descriptor .serialize ();
203
- @memcpy (S .tmp [used .. used + hd .len ], & hd );
204
- used += hd .len ;
205
- }
193
+ // round trips.
194
+ var bw = BufferWriter { .buffer = & S .tmp };
195
+ try bw .write (& usb_config .? .config_descriptor .serialize ());
196
+ try bw .write (& usb_config .? .interface_descriptor .serialize ());
206
197
207
- // TODO: depending on the number of endpoints
208
- // this might not fit in 64 bytes -> split message
209
- // into multiple packets
210
- for (usb_config .? .endpoints [2.. ]) | ep | {
211
- const ed = ep .descriptor .serialize ();
212
- @memcpy (S .tmp [used .. used + ed .len ], & ed );
213
- used += ed .len ;
214
- }
198
+
199
+ // Seems like the host does not bother asking for the
200
+ // hid descriptor so we'll just send it with the
201
+ // other descriptors.
202
+ if (usb_config .? .hid ) | hid_conf | {
203
+ try bw .write (& hid_conf .hid_descriptor .serialize ());
215
204
}
216
205
217
- // Set up EP0 IN to send the stuff we just composed.
218
- f . usb_start_tx (
219
- usb_config .? . endpoints [ EP0_IN_IDX ],
220
- S . tmp [0 .. used ],
221
- );
206
+ for ( usb_config .? . endpoints [2 .. ]) | ep | {
207
+ try bw . write ( & ep . descriptor . serialize ());
208
+ }
209
+
210
+ CmdEndpoint . send_cmd_response ( bw . get_written_slice (), setup . length );
222
211
},
223
212
.String = > {
224
213
if (debug ) std .log .info (" String" , .{});
@@ -235,19 +224,16 @@ pub fn Usb(comptime f: anytype) type {
235
224
const s = usb_config .? .descriptor_strings [i - 1 ];
236
225
const len = 2 + s .len ;
237
226
238
- S .tmp [0 ] = @intCast (len );
239
- S .tmp [1 ] = 0x03 ;
240
- @memcpy (S .tmp [2.. len ], s );
227
+ var wb = BufferWriter { .buffer = & S .tmp };
228
+ try wb .write_int (u8 , @intCast (len ));
229
+ try wb .write_int (u8 , 0x03 );
230
+ try wb .write (s );
241
231
242
- break :StringBlk S . tmp [0 .. len ] ;
232
+ break :StringBlk wb . get_written_slice () ;
243
233
}
244
234
};
245
- // Set up EP0 IN to send whichever thing we just
246
- // decided on.
247
- f .usb_start_tx (
248
- usb_config .? .endpoints [EP0_IN_IDX ],
249
- bytes ,
250
- );
235
+
236
+ CmdEndpoint .send_cmd_response (bytes , setup .length );
251
237
},
252
238
.Interface = > {
253
239
if (debug ) std .log .info (" Interface" , .{});
@@ -278,13 +264,10 @@ pub fn Usb(comptime f: anytype) type {
278
264
.num_configurations = usb_config .? .device_descriptor .num_configurations ,
279
265
};
280
266
281
- const data = dqd . serialize () ;
282
- @memcpy ( S . tmp [0 .. data . len ], & data );
267
+ var bw = BufferWriter { . buffer = & S . tmp } ;
268
+ try bw . write ( & dqd . serialize () );
283
269
284
- f .usb_start_tx (
285
- usb_config .? .endpoints [EP0_IN_IDX ],
286
- S .tmp [0.. data .len ],
287
- );
270
+ CmdEndpoint .send_cmd_response (bw .get_written_slice (), setup .length );
288
271
},
289
272
}
290
273
} else {
@@ -298,23 +281,17 @@ pub fn Usb(comptime f: anytype) type {
298
281
.Hid = > {
299
282
if (debug ) std .log .info (" HID" , .{});
300
283
301
- const hd = hid_conf . hid_descriptor . serialize () ;
302
- @memcpy ( S . tmp [0 .. hd . len ], & hd );
284
+ var bw = BufferWriter { . buffer = & S . tmp } ;
285
+ try bw . write ( & hid_conf . hid_descriptor . serialize () );
303
286
304
- f .usb_start_tx (
305
- usb_config .? .endpoints [EP0_IN_IDX ],
306
- S .tmp [0.. hd .len ],
307
- );
287
+ CmdEndpoint .send_cmd_response (bw .get_written_slice (), setup .length );
308
288
},
309
289
.Report = > {
310
290
if (debug ) std .log .info (" Report" , .{});
311
291
312
292
// The report descriptor is already a (static)
313
293
// u8 array, i.e., we can pass it directly
314
- f .usb_start_tx (
315
- usb_config .? .endpoints [EP0_IN_IDX ],
316
- hid_conf .report_descriptor ,
317
- );
294
+ CmdEndpoint .send_cmd_response (hid_conf .report_descriptor , setup .length );
318
295
},
319
296
.Physical = > {
320
297
if (debug ) std .log .info (" Physical" , .{});
@@ -352,11 +329,31 @@ pub fn Usb(comptime f: anytype) type {
352
329
switch (epb .endpoint .descriptor .endpoint_address ) {
353
330
EP0_IN_ADDR = > {
354
331
if (debug ) std .log .info (" EP0_IN_ADDR" , .{});
332
+
333
+ const cmd_in_endpoint = usb_config .? .endpoints [EP0_IN_IDX ];
334
+ const buffer_reader = & S .buffer_reader ;
335
+
355
336
// We use this opportunity to finish the delayed
356
337
// SetAddress request, if there is one:
357
338
if (S .new_address ) | addr | {
358
339
// Change our address:
359
340
f .set_address (@intCast (addr ));
341
+ }
342
+
343
+ if (epb .buffer .len > 0 and buffer_reader .get_remaining_bytes_count () > 0 ) {
344
+ _ = buffer_reader .try_advance (epb .buffer .len );
345
+ const next_data_chunk = buffer_reader .try_read (cmd_in_endpoint .descriptor .max_packet_size );
346
+ if (next_data_chunk .len > 0 ) {
347
+ f .usb_start_tx (
348
+ cmd_in_endpoint ,
349
+ next_data_chunk ,
350
+ );
351
+ } else {
352
+ f .usb_start_rx (
353
+ usb_config .? .endpoints [EP0_OUT_IDX ], // EP0_OUT_CFG,
354
+ 0 ,
355
+ );
356
+ }
360
357
} else {
361
358
// Otherwise, we've just finished sending
362
359
// something to the host. We expect an ensuing
@@ -393,6 +390,7 @@ pub fn Usb(comptime f: anytype) type {
393
390
S .new_address = null ;
394
391
S .configured = false ;
395
392
S .started = false ;
393
+ S .buffer_reader = BufferReader { .buffer = &.{} };
396
394
}
397
395
398
396
// If we have been configured but haven't reached this point yet, set up
@@ -842,6 +840,93 @@ pub const EPBIter = struct {
842
840
next : * const fn (self : * @This ()) ? EPB ,
843
841
};
844
842
843
+ const BufferWriter = struct {
844
+ buffer : []u8 ,
845
+ pos : usize = 0 ,
846
+ endian : std.builtin.Endian = builtin .cpu .arch .endian (),
847
+
848
+ pub const Error = error { EndOfBuffer };
849
+
850
+ /// Moves forward write cursor by the provided number of bytes.
851
+ pub fn advance (self : * @This (), bytes : usize ) Error ! void {
852
+ try self .bound_check (bytes );
853
+ self .advance_unsafe (bytes );
854
+ }
855
+
856
+ /// Writes data provided as a slice to the buffer and moves write cursor forward by data size.
857
+ pub fn write (self : * @This (), data : []const u8 ) Error ! void {
858
+ try self .bound_check (data .len );
859
+ defer self .advance_unsafe (data .len );
860
+ @memcpy (self .buffer [self .pos .. self .pos + data .len ], data );
861
+ }
862
+
863
+ /// Writes an int with respect to the buffer's endianness and moves write cursor forward by int size.
864
+ pub fn write_int (self : * @This (), comptime T : type , value : T ) Error ! void {
865
+ const size = @divExact (@typeInfo (T ).Int .bits , 8 );
866
+ try self .bound_check (size );
867
+ defer self .advance_unsafe (size );
868
+ std .mem .writeInt (T , self .buffer [self .pos .. ][0.. size ], value , self .endian );
869
+ }
870
+
871
+ /// Writes an int with respect to the buffer's endianness but skip bound check.
872
+ /// Useful in cases where the bound can be checked once for batch of ints.
873
+ pub fn write_int_unsafe (self : * @This (), comptime T : type , value : T ) void {
874
+ const size = @divExact (@typeInfo (T ).Int .bits , 8 );
875
+ defer self .advance_unsafe (size );
876
+ std .mem .writeInt (T , self .buffer [self .pos .. ][0.. size ], value , self .endian );
877
+ }
878
+
879
+ /// Returns a slice of the internal buffer containing the written data.
880
+ pub fn get_written_slice (self : * const @This ()) []const u8 {
881
+ return self .buffer [0.. self .pos ];
882
+ }
883
+
884
+ /// Performs a buffer bound check against the current cursor position and the provided number of bytes to check forward.
885
+ pub fn bound_check (self : * const @This (), bytes : usize ) Error ! void {
886
+ if (self .pos + bytes > self .buffer .len ) return error .EndOfBuffer ;
887
+ }
888
+
889
+ fn advance_unsafe (self : * @This (), bytes : usize ) void {
890
+ self .pos += bytes ;
891
+ }
892
+ };
893
+
894
+ const BufferReader = struct {
895
+ buffer : []const u8 ,
896
+ pos : usize = 0 ,
897
+ endian : std.builtin.Endian = builtin .cpu .arch .endian (),
898
+
899
+ /// Attempts to move read cursor forward by the specified number of bytes.
900
+ /// Returns the actual number of bytes advanced, up to the specified number.
901
+ pub fn try_advance (self : * @This (), bytes : usize ) usize {
902
+ const size = @min (bytes , self .buffer .len - self .pos );
903
+ self .advance_unsafe (size );
904
+ return size ;
905
+ }
906
+
907
+ /// Attempts to read the given amount of bytes (or less if close to buffer end) and advances the read cursor.
908
+ pub fn try_read (self : * @This (), bytes : usize ) []const u8 {
909
+ const size = @min (bytes , self .buffer .len - self .pos );
910
+ defer self .advance_unsafe (size );
911
+ return self .buffer [self .pos .. self .pos + size ];
912
+ }
913
+
914
+ /// Attempts to read the given amount of bytes (or less if close to buffer end) without advancing the read cursor.
915
+ pub fn try_peek (self : * @This (), bytes : usize ) []const u8 {
916
+ const size = @min (bytes , self .buffer .len - self .pos );
917
+ return self .buffer [self .pos .. self .pos + size ];
918
+ }
919
+
920
+ /// Returns the number of bytes remaining from the current read cursor position to the end of the underlying buffer.
921
+ pub fn get_remaining_bytes_count (self : * const @This ()) usize {
922
+ return self .buffer .len - self .pos ;
923
+ }
924
+
925
+ fn advance_unsafe (self : * @This (), bytes : usize ) void {
926
+ self .pos += bytes ;
927
+ }
928
+ };
929
+
845
930
/// Convert an utf8 into an utf16 (little endian) string
846
931
pub fn utf8Toutf16Le (comptime s : []const u8 ) [s.len << 1 ]u8 {
847
932
const l = s .len << 1 ;
0 commit comments