/
USB_drv.c
2026 lines (1918 loc) · 128 KB
/
USB_drv.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
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
/**********************************************************************
Mark Butcher Bsc (Hons) MPhil MIET
M.J.Butcher Consulting
Birchstrasse 20f, CH-5406, Rütihof
Switzerland
www.uTasker.com Skype: M_J_Butcher
---------------------------------------------------------------------
File: USB_drv.c
Project: Single Chip Embedded Internet
---------------------------------------------------------------------
Copyright (C) M.J.Butcher Consulting 2004..2018
*********************************************************************
06.10.2008 Adjust USB read/writes to support FIFO operation {1}
17.10.2008 Add control endpoint direction control and setup clear {2}
25.12.2008 Additional tx buffer depth check (no longer optional) {3}
04.01.2009 Additional setup clears added {4}
10.01.2009 Correct tx buffer depth check {5}
10.01.2009 Break from loop rather than individual return values {6}
29.01.2009 Clear endpoint FIFO depth on disconnect {7}
07.03.2009 Remove unnecessary usEndPoints {8}
12.05.2009 Optionally don't sent zero-data frame on non-control endpoints {9}
02.02.2010 Sum outstanding data when multiple buffers {10}
21.03.2010 Allow control of whether an endpoint sends zero-date frame{11}
21.03.2010 Allow receptions to be handled by both a callback and a buffer {12}
21.03.2010 Add USB_REQUEST_CLEAR_FEATURE support {13}
21.03.2010 Modify the routine fnSetUSBEndpointState() to only set state bits {14}
22.03.2010 Extract indexed data from FIFO, when FIFO used {15}
02.04.2010 Add VALIDATE_NEW_CONFIGURATION() and fnGetPairedIN() {16}
18.04.2010 Add USB DMA support {17}
20.06.2010 Add CRITICAL_OUT for FIFO endpoints which have to consume data {18}
13.07.2010 Add intermediate FIFO support {19}
14.02.2011 Configure usb_endpoint_control before configuring hardware to avoid possibility of reset interrupt trying to use it too soon {20}
15.02.2012 Correct queuing for devices with more than double-buffer depth {21} (this correction isn't important for devices previously supported up to its introduction)
15.02.2012 Add MULTIPLE_TX option for devices that can independently handle multiple IN packets {22}
04.06.2013 Added USB_DRV_MALLOC() default {23}
06.06.2013 Remove device qualifier descriptor when USB1.1 is used {24}
14.08.2013 Add option for high speed devices than control multiple IN packets {25}
14.01.2015 Add capability to share IN/OUT endpoints (USB_SIMPLEX_ENDPOINTS)
03.03.2015 Only send zero frame to acknowledgement of block sizes equal to the endpoint size when it is a control or terminating endpoint {26}
08.06.2015 Add optional IN_COMPLETE_CALLBACK to callback on successful IN frame (useful for interrupt endpoints to known when the last host poll has completed) {27}
04.10.2015 Change fnGetUSB_HW() parameter from pointer to pointer to pointer {28}
21.10.2015 Supports device or host or both host and device {29}
02.12.2015 Add prtUSBcontrolSetup to allow automating zero termination on data sent by application handling class setup IN frames {30}
23.12.2015 Add zero copy operation {31}
27.01.2016 Add USB tx message mode operation {32}
13.04.2016 Change parameter of fnGetUSB_string_entry() to unsigned char {33}
12.04.2017 In host mode reset previous string reception length counter when reception completes with a zero length frame {34}
12.04.2017 Use USB_DEVICE_TIMEOUT in host mode to repeat a get descriptor request {35}
*/
/* =================================================================== */
/* include files */
/* =================================================================== */
#include "config.h"
#if defined USB_INTERFACE
//#define _USB_CATCH_ERROR // debug define to catch FIFO errors
//#define SET_INTERFACE
#if defined _USB_CATCH_ERROR
static void fnError(int iErrorNumber)
{
FOREVER_LOOP() {}
}
#endif
/* =================================================================== */
/* local definitions */
/* =================================================================== */
#if !defined USB_DRV_MALLOC // {23}
#define USB_DRV_MALLOC(x) uMalloc((MAX_MALLOC)(x))
#endif
#define DIRECTION_OUT() ((ucFirstByte & STANDARD_DEVICE_TO_HOST) == 0)
#define DIRECTION_IN() ((ucFirstByte & STANDARD_DEVICE_TO_HOST) != 0)
#if defined USB_FIFO && !defined _WINDOWS // {1}
#define GET_USB_DATA() (unsigned char)(*(volatile unsigned char *)ptrData)
#define GET_USB_DATA_NO_INC() (unsigned char)(*(volatile unsigned char *)ptrData)
#else
#define GET_USB_DATA() *ptrData++
#define GET_USB_DATA_NO_INC() *ptrData
#endif
#if defined USB_HOST_SUPPORT // {29}
#define USB_DEVICE_ADDRESS 1 // single USB device with fixed address (allocated when host)
#define _DEVICE_DATA 0
#define _HOST_DATA 1
#define _DEVICE_HOST_DATA 2 // data (not setup) in device or host mode
#define _DEVICE_DATA_HOST_SETUP 3 // either device/host data or host setup
#define _HOST_SETUP 4 // definitely a setup in host mode
#define DEVICE_DATA _DEVICE_DATA,
#define HOST_DATA _HOST_DATA,
#define DEVICE_HOST_DATA _DEVICE_HOST_DATA,
#define DEVICE_DATA_HOST_SETUP _DEVICE_DATA_HOST_SETUP,
#define HOST_SETUP _HOST_SETUP,
#define HOST_ENUMERATION_IDLE 0 // host state-event-machine states
#define HOST_ENUMERATION_STANDARD_DEVICE_DESCRIPTOR 1
#define HOST_ENUMERATION_STANDARD_DEVICE_DESCRIPTOR_ACK 2
#define HOST_ENUMERATION_SET_ADDRESS 3
#define HOST_ENUMERATION_CONFIGURATION_DESCRIPTOR 4
#define HOST_ENUMERATION_CONFIGURATION_DESCRIPTOR_ACK 5
#define HOST_ENUMERATION_REQUEST_STRING 6
#define HOST_ENUMERATION_REQUEST_STRING_ACK 7
#define HOST_ENUMERATION_SET_CONFIGURATION 8
#define HOST_ENUMERATION_SET_CONFIGURATION_ACK 9
#define HOST_ENUMERATION_SET_INTERFACE_ACK 10
#define HOST_ENUMERATION_CONFIGURED 11
#define HOST_ENUMERATION_CONFIGURED_ACK 12
#define HOST_ENUMERATION_CONFIGURED_IDLE 13
#define SUCCESSFUL_TERMINATION 0 // host state-event-machine events
#define ZERO_DATA_TERMINATOR 1
#define DEVICE_DETECTED 2
#define DEVICE_TIMEOUT 3
#define DATA_RECEPTION_COMPLETE 4
#define HOST_APPLICATION_ON 5
#define HOST_APPLICATION_OFF 6
#else // not used in device only mode
#define DEVICE_DATA
#define HOST_DATA
#define DEVICE_HOST_DATA
#define DEVICE_DATA_HOST_SETUP
#define DATA_HOST_SETUP
#define USB_DEVICE_SUPPORT // enable device only support if host support hasn't specifically defined
#endif
/* =================================================================== */
/* local structure definitions */
/* =================================================================== */
/* =================================================================== */
/* local function prototype declarations */
/* =================================================================== */
/* =================================================================== */
/* constants */
/* =================================================================== */
static const unsigned short usInterfaceStatus = 0x0000; // the interface status is presently reserved in the USB specification so a fixed value of zeros can be used
static const unsigned short usEndpointStalled = LITTLE_SHORT_WORD(ENDPOINT_STATUS_STALLED);
#define usEndpointOK usInterfaceStatus // use the same zero status
/* =================================================================== */
/* local variable definitions */
/* =================================================================== */
static USB_ENDPOINT *usb_endpoint_control = 0; // pointer to a list of endpoint control structures
static unsigned char ucActiveConfiguration = 0; // present active configuration
//static unsigned char usEndPoints = 0; // {8} the number of endpoints configured
#if defined USB_DEVICE_SUPPORT
static unsigned short usDeviceStatus = 0; // state of device (SELF_POWERED_BIT, REMOTE_WAKEUP_BIT)
static USB_SETUP_HEADER *prtUSBcontrolSetup = 0; // {30}
#endif
#if defined USB_HOST_SUPPORT // {29}
static USB_DEVICE_DESCRIPTOR device_descriptor = {0}; // device descriptor of connected device
static unsigned char ucRequestType = HOST_ENUMERATION_IDLE;
static unsigned char ucRequestingReceived = 0;
static unsigned char ucRequestLengthRemaining = 0;
static unsigned char ucStalledEndpoint = 0;
static unsigned char config_descriptor[255] = {0};
static unsigned char ucConfigDescriptorLength = 0;
#if defined USB_STRING_OPTION
static unsigned char ucStringList[USB_MAX_STRINGS + 1][(USB_MAX_STRING_LENGTH + 1) * 2] = {{0}}; // unicode string space (first is language ID)
static int iStringReference = 0;
#endif
#endif
/* =================================================================== */
/* local function definitions */
/* =================================================================== */
#if defined USB_HOST_SUPPORT // {29}
static QUEUE_TRANSFER fnPrepareOutData(int iMode, unsigned char *ptrData, unsigned short usLength, unsigned short ucMaxLength, int iEndpoint, USB_HW *ptrUSB_HW);
#else
static QUEUE_TRANSFER fnPrepareOutData(unsigned char *ptrData, unsigned short usLength, unsigned short ucMaxLength, int iEndpoint, USB_HW *ptrUSB_HW);
#endif
#if defined USB_DEVICE_SUPPORT // {29}
static void *fnSetUSBInterfaceState(int iCommand, unsigned char ucInterface, unsigned char ucAlternativeInterface);
#define USB_INTERFACE_DEACTIVATE 0
#define USB_INTERFACE_ACTIVATE 1
#define USB_INTERFACE_ALTERNATE 2
#endif
#if defined USB_HOST_SUPPORT // {29}
static int fnHostEmumeration(int iEndpoint, int iEvent, USB_HW *ptrUSB_HW);
#endif
/* =================================================================== */
/* global function definitions */
/* =================================================================== */
// Standard entry call to driver - dispatches required sub-routine
//
extern QUEUE_TRANSFER entry_usb(QUEUE_HANDLE channel, unsigned char *ptBuffer, QUEUE_TRANSFER Counter, unsigned char ucCallType, QUEUE_HANDLE DriverID)
{
QUEUE_TRANSFER rtn_val = 0;
USBQUE *ptrUsbQueue;
uDisable_Interrupt(); // disable all interrupts
switch (ucCallType) {
case CALL_DRIVER: // request changes and return status
if ((TX_ON & Counter) != 0) { // enable a configuration
#if defined USB_HOST_SUPPORT
ucActiveConfiguration = (unsigned char)(CAST_POINTER_ARITHMETIC)ptBuffer; // set the configuration number that will be activated
if (fnHostEmumeration(0, HOST_APPLICATION_ON, 0) == USB_HOST_ERROR) {
ucActiveConfiguration = 0;
}
#else
ptrUsbQueue = (USBQUE *)que_ids[DriverID].output_buffer_control; // set to output control block
// Enable continuous isochronous transmission
//
if ((ptrUsbQueue->endpoint_control->ucState & TX_ACTIVE) == 0) { // if transmission is already in progress don't initiate any more activity
ptrUsbQueue->USB_queue.get = ptrUsbQueue->USB_queue.QUEbuffer;
ptrUsbQueue->USB_queue.chars = ptrUsbQueue->USB_queue.buf_length;
rtn_val = fnStartUSB_send(channel, ptrUsbQueue, ptrUsbQueue->USB_queue.chars);
}
#endif
break;
}
else if ((TX_OFF & Counter) != 0) { // disable a configuration
#if defined USB_HOST_SUPPORT
ucActiveConfiguration = 0;
fnHostEmumeration(0, HOST_APPLICATION_OFF, 0);
#else
// Disable continuous isochronous transmission
//
ptrUsbQueue = (USBQUE *)que_ids[DriverID].output_buffer_control; // set to output control block
ptrUsbQueue->endpoint_control->ucState &= ~(TX_ACTIVE);
#endif
break;
}
#if defined USB_HOST_SUPPORT // {29}
if ((RX_ON & Counter) != 0) { // enable host IN polling on this endpoint
ptrUsbQueue = (USBQUE *)(que_ids[DriverID].input_buffer_control); // set to input control block
uEnable_Interrupt(); // don't block interrupts when starting IN poll
fnHostEndpoint(ptrUsbQueue->endpoint_control->ucEndpointNumber, IN_POLLING, 1);
uDisable_Interrupt();
}
else if ((RX_OFF & Counter) != 0) { // disable host IN polling on this endpoint
ptrUsbQueue = (USBQUE *)(que_ids[DriverID].input_buffer_control); // set to input control block
uEnable_Interrupt(); // don't block interrupts when stopping IN poll
fnHostEndpoint(ptrUsbQueue->endpoint_control->ucEndpointNumber, IN_POLLING, 0);
uDisable_Interrupt();
}
#endif
if (((CAST_POINTER_ARITHMETIC)ptBuffer & MODIFY_TX) != 0) {
ptrUsbQueue = (USBQUE *)(que_ids[DriverID].output_buffer_control); // set to output control block
}
else {
ptrUsbQueue = (USBQUE *)(que_ids[DriverID].input_buffer_control); // set to input control block
}
if ((Counter & MODIFY_WAKEUP) != 0) {
if ((ptrUsbQueue->endpoint_control->ucState & USB_ENDPOINT_ACTIVE) != 0) { // don't allow changes when endpoint is not active
#if defined USB_SIMPLEX_ENDPOINTS || defined SUPPORT_USB_SIMPLEX_HOST_ENDPOINTS
ptrUsbQueue->endpoint_control->event_task_in = (UTASK_TASK)((CAST_POINTER_ARITHMETIC)ptBuffer & 0x7f);
#else
ptrUsbQueue->endpoint_control->event_task = (UTASK_TASK)((CAST_POINTER_ARITHMETIC)ptBuffer & 0x7f);
#endif
}
}
rtn_val = ptrUsbQueue->endpoint_control->ucState; // return the present state
break;
case CALL_INPUT: // request the number or input characters waiting
ptrUsbQueue = (USBQUE *)(que_ids[DriverID].input_buffer_control);// set to input control block
rtn_val = ptrUsbQueue->USB_queue.chars;
break;
case CALL_WRITE: // write data into the output queue
// copy the data to the output buffer and start transmission if not already done
ptrUsbQueue = (USBQUE *)que_ids[DriverID].output_buffer_control; // set to output control block
if (ptrUsbQueue == 0) { // no output buffer so use direct method
USB_HW temp_usb_hardware; // temporary copy of the hardware information
USB_HW *ptr_usb_hardware = &temp_usb_hardware; // {28}
if (fnGetUSB_HW(channel, &ptr_usb_hardware) == ENDPOINT_FREE) { // if hardware is ready and buffers not full (the routine fills out needed hardware information)
unsigned short usMaxLength = Counter;
#if defined USB_DEVICE_SUPPORT
if ((channel == 0) && (prtUSBcontrolSetup != 0)) { // {30} if device control endpoint 0 is sending setup IN data
usMaxLength = (prtUSBcontrolSetup->wLength[0] | (prtUSBcontrolSetup->wLength[1] << 8)); // pass the limit so that zero termination can be handled appropriately
}
#endif
rtn_val = fnPrepareOutData(DEVICE_DATA_HOST_SETUP ptBuffer, Counter, usMaxLength, channel, ptr_usb_hardware);
}
break;
}
if ((ptrUsbQueue->endpoint_control->ucState & USB_ENDPOINT_ACTIVE) == 0) { // only allow the copy if the endpoint is active
break;
}
if (ptBuffer == 0) { // the caller wants to see whether the data will fit and not copy data so inform
#if defined USB_TX_MESSAGE_MODE
if (ptrUsbQueue->endpoint_control->messageQueue != 0) { // {32}
Counter += (ptrUsbQueue->endpoint_control->usMax_frame_length - 1); // assume worst case where a single byte will be located in the final frame buffer
if ((ptrUsbQueue->USB_queue.buf_length - ptrUsbQueue->USB_queue.chars) >= Counter) { // if there is space for the message
if ((ptrUsbQueue->USB_queue.chars != 0) && (ptrUsbQueue->endpoint_control->messageQueue->ucInIndex == ptrUsbQueue->endpoint_control->messageQueue->ucOutIndex)) { // if the fifo is full
break;
}
else {
rtn_val = (ptrUsbQueue->USB_queue.buf_length - ptrUsbQueue->USB_queue.chars - (ptrUsbQueue->endpoint_control->usMax_frame_length - 1)); // the remaining space
}
}
}
else {
#endif
if ((ptrUsbQueue->USB_queue.buf_length - ptrUsbQueue->USB_queue.chars) >= Counter) {
rtn_val = (ptrUsbQueue->USB_queue.buf_length - ptrUsbQueue->USB_queue.chars); // the remaining space
}
#if defined USB_TX_MESSAGE_MODE
}
#endif
}
else { // buffered mode
#if defined USB_TX_MESSAGE_MODE
if (ptrUsbQueue->endpoint_control->messageQueue != 0) { // {32}
unsigned char *ptrTo = ptrUsbQueue->USB_queue.put;
QUEUE_TRANSFER remainingSpace = (ptrUsbQueue->USB_queue.buffer_end - ptrTo); // remaining space to the end of the buffer
if (Counter <= remainingSpace) { // if the message fits into the remaining buffer space
remainingSpace -= Counter; // remaining space after the copy
if ((remainingSpace != 0) && (remainingSpace < ptrUsbQueue->endpoint_control->usMax_frame_length)) { // if the remaining length will be less that the endpont length
ptrUsbQueue->USB_queue.put = ptrUsbQueue->USB_queue.QUEbuffer; // next message will start at the beginning of the buffer (some space at the end fo the buffer will not be used)
}
else {
ptrUsbQueue->USB_queue.put += Counter; // following message will be located here
}
ptrUsbQueue->USB_queue.chars += Counter; // new total bytes waiting to be sent
ptrUsbQueue->endpoint_control->messageQueue->usLength[ptrUsbQueue->endpoint_control->messageQueue->ucInIndex++] = Counter; // set the length to the length fifo
if (ptrUsbQueue->endpoint_control->messageQueue->ucInIndex >= ptrUsbQueue->endpoint_control->messageQueue->ucQuantity) { // handle length fifo overflow
ptrUsbQueue->endpoint_control->messageQueue->ucInIndex = 0;
}
uEnable_Interrupt(); // allow interrupts during copy to the buffer since the critical counters and pointers have been prepared for the state after the copy can completed
uMemcpy(ptrTo, ptBuffer, Counter); // copy data to reserved area without blocked interrupts
uDisable_Interrupt(); // disable interrupts again for compatibility
}
else { // wrap results
int iSpace = (remainingSpace % ptrUsbQueue->endpoint_control->usMax_frame_length); // the space at the end of the buffer that must be left unused
int iRemainingCopy;
remainingSpace -= iSpace; // the length of the first block (divisible by the endpoint length)
iRemainingCopy = (Counter - remainingSpace);
ptrUsbQueue->USB_queue.put = (ptrUsbQueue->USB_queue.QUEbuffer + iRemainingCopy);
ptrUsbQueue->endpoint_control->messageQueue->usLength[ptrUsbQueue->endpoint_control->messageQueue->ucInIndex++] = Counter; // set the length to the length fifo
if (ptrUsbQueue->endpoint_control->messageQueue->ucInIndex >= ptrUsbQueue->endpoint_control->messageQueue->ucQuantity) { // handle length fifo overflow
ptrUsbQueue->endpoint_control->messageQueue->ucInIndex = 0;
}
ptrUsbQueue->USB_queue.chars += Counter; // new total bytes waiting to be sent
uEnable_Interrupt(); // allow interrupts
uMemcpy(ptrTo, ptBuffer, remainingSpace); // copy data to reserved area without blocked interrupts
ptBuffer += remainingSpace;
uMemcpy(ptrUsbQueue->USB_queue.QUEbuffer, ptBuffer, iRemainingCopy); // complete copy of second part of the block
uDisable_Interrupt();
}
rtn_val = Counter; // message size to be sent
}
else {
#endif
uEnable_Interrupt(); // fnFillBuffer disables and then re-enables interrupts - be sure we are compatible
rtn_val = fnFillBuf(&ptrUsbQueue->USB_queue, ptBuffer, Counter); // copy the input data to the output circular buffer
uDisable_Interrupt();
#if defined USB_TX_MESSAGE_MODE
}
#endif
if ((ptrUsbQueue->endpoint_control->ucState & TX_ACTIVE) == 0) { // if transmission is already in progress don't initiate any more activity
rtn_val = fnStartUSB_send(channel, ptrUsbQueue, rtn_val);// start sending message content
}
}
break;
case CALL_READ: // read data from the queue
ptrUsbQueue = (USBQUE *)(que_ids[DriverID].input_buffer_control);// set to input control block
rtn_val = fnGetBuf(&ptrUsbQueue->USB_queue, ptBuffer, Counter); // interrupts are re-enabled as soon as no longer critical
if ((ptrUsbQueue->endpoint_control->ucState & USB_ENDPOINT_BLOCKED) != 0) { // the buffer has been previously blocked due to lack of space
uDisable_Interrupt();
ptrUsbQueue->endpoint_control->ucState &= ~USB_ENDPOINT_BLOCKED; // remove the blocked state - it will be set again if the data in the input USB buffer still can't be put to the buffer
while (fnConsumeUSB_out(ptrUsbQueue->endpoint_control->ucEndpointNumber) == USB_BUFFER_FREED) {} // copy data from waiting buffer and free buffer for further use
uEnable_Interrupt();
}
return rtn_val; // the amount of data returned
#if defined SUPPORT_FLUSH
case CALL_FLUSH: // flush input or output queue completely
if (Counter != FLUSH_RX) { // tx
ptrUsbQueue = (USBQUE *)(que_ids[DriverID].output_buffer_control); // set to output control block
}
else { // rx
ptrUsbQueue = (USBQUE *)(que_ids[DriverID].input_buffer_control); // set to input control block
}
ptrUsbQueue->USB_queue.get = ptrUsbQueue->USB_queue.put = ptrUsbQueue->USB_queue.QUEbuffer;
ptrUsbQueue->USB_queue.chars = 0;
break;
#endif
default:
break;
}
uEnable_Interrupt(); // enable interrupts
return (rtn_val);
}
extern QUEUE_TRANSFER fnStartUSB_send(QUEUE_HANDLE channel, USBQUE *ptrUsbQueue, QUEUE_TRANSFER txLength)
{
#if defined USB_DMA_TX && defined USB_RAM_START
ptrUsbQueue->endpoint_control->ucState |= TX_ACTIVE; // mark that the transmitter is active
(usb_endpoint_control + channel)->usCompleteMessage = (usb_endpoint_control + channel)->usSent = txLength = fnPrepareUSBOutData(ptrUsbQueue, txLength, channel, ptr_usb_hardware);
return txLength;
#else
USB_HW temp_usb_hardware; // temporary copy of the present hardware information
USB_HW *ptr_usb_hardware = &temp_usb_hardware; // {28}
if (fnGetUSB_HW(channel, &ptr_usb_hardware) == ENDPOINT_FREE) { // check that there is a free buffer to send with
unsigned short usLength;
ptrUsbQueue->endpoint_control->ucState |= TX_ACTIVE; // mark that the transmitter is active
#if defined USB_TX_MESSAGE_MODE
if (ptrUsbQueue->endpoint_control->messageQueue != 0) { // {32}
usLength = txLength; // in message mode we always handle each new transmission as a single message, even if it involves circular buffer operation
}
else {
#endif
usLength = (ptrUsbQueue->USB_queue.buffer_end - ptrUsbQueue->USB_queue.get); // maximum linear part of buffer
if (txLength < usLength) { // if the requested transmission length is less that the maximum possible linear length
usLength = txLength; // set requested length
}
#if defined USB_TX_MESSAGE_MODE
}
#endif
return (fnPrepareOutData(DEVICE_HOST_DATA ptrUsbQueue->USB_queue.get, usLength, usLength, channel, ptr_usb_hardware));
}
else { // the transmitter is not busy but it was not possible to send the data
return (0); // signal that there was a problem that may need to be handled
}
#endif
}
// fnOpen() calls this for the USB interface
//
extern QUEUE_HANDLE fnOpenUSB(USBTABLE *pars, unsigned char driver_mode)
{
QUEUE_HANDLE DriverID;
QUEUE_TRANSFER (*entry_add)(QUEUE_HANDLE channel, unsigned char *ptBuffer, QUEUE_TRANSFER Counter, unsigned char ucCallType, QUEUE_HANDLE DriverID) = entry_usb;
USB_ENDPOINT *usb_endpoint_queue = usb_endpoint_control;
#if defined USB_HOST_SUPPORT && defined USB_DEVICE_SUPPORT
if ((DriverID = fnSearchID(entry_add, pars->Endpoint)) == NO_ID_ALLOCATED) {
DriverID = fnAllocateQueue(&pars->queue_sizes, pars->Endpoint, entry_add, sizeof(USBQUE)); // allocate queue and buffers
}
#else
DriverID = fnAllocateQueue(&pars->queue_sizes, pars->Endpoint, entry_add, sizeof(USBQUE)); // allocate queue and buffers
#endif
if (pars->Endpoint != 0) { // endpoint 0, used for general configuration
usb_endpoint_queue += pars->Endpoint;
usb_endpoint_queue->usMax_frame_length = pars->usEndpointSize; // enter end point limit
if ((pars->usConfig & USB_TERMINATING_ENDPOINT) != 0) { // {11}
usb_endpoint_queue->usParameters = USB_ENDPOINT_TERMINATES; // set endpoint's mode parameter
}
if ((pars->usConfig & USB_OUT_ZERO_COPY) != 0) { // {31}
usb_endpoint_queue->usParameters |= USB_ENDPOINT_ZERO_COPY_OUT; // set endpoint's mode parameter
}
if ((pars->usConfig & USB_IN_ZERO_COPY) != 0) { // {31}
usb_endpoint_queue->usParameters |= USB_ENDPOINT_ZERO_COPY_IN; // set endpoint's mode parameter
}
#if defined USB_TX_MESSAGE_MODE
if ((pars->usConfig & USB_IN_MESSAGE_MODE) != 0) { // {32}
unsigned char ucQuantity = (unsigned char)((pars->usConfig & USB_IN_FIFO_MASK) >> USB_IN_FIFO_SHIFT);
usb_endpoint_queue->usParameters |= USB_ENDPOINT_IN_MESSAGE_MODE; // set endpoint's mode parameter
usb_endpoint_queue->messageQueue = (USB_MESSAGE_QUEUE *)USB_DRV_MALLOC(sizeof(USB_MESSAGE_QUEUE) + ((ucQuantity - 1) * sizeof(unsigned short)));
usb_endpoint_queue->messageQueue->ucQuantity = ucQuantity; // message fifo size
}
#endif
#if defined IN_COMPLETE_CALLBACK // {27}
usb_endpoint_queue->fnINcomplete = pars->INcallback; // call back to be used when an IN frame has completed
#endif
}
else { // default control endpoint
//fnConfigUSB(0, pars); // configure hardware for control end point
//usEndPoints = (pars->ucEndPoints + 1); // {8}
usb_endpoint_control = usb_endpoint_queue = USB_DRV_MALLOC(((pars->ucEndPoints + 1) * sizeof(USB_ENDPOINT))); // {8} create the control queue for all end points
usb_endpoint_queue->usMax_frame_length = ENDPOINT_0_SIZE; // enter end point 0 limit
usb_endpoint_queue->event_task = pars->owner_task; // the task notified of USB state changes
fnConfigUSB(0, pars); // configure hardware for control end point {20}
}
if ((usb_endpoint_queue->ptrEndpointInCtr = (que_ids[DriverID - 1].output_buffer_control)) != 0) { // set to output control block
((USBQUE *)(usb_endpoint_queue->ptrEndpointInCtr))->endpoint_control = usb_endpoint_queue;
#if defined WAKE_BLOCKED_USB_TX
usb_endpoint_queue->low_water_level = pars->low_water_level;
//usb_endpoint_queue->event_task = 0; // uMalloc returns this as zero
#endif
}
usb_endpoint_queue->ucEndpointNumber = pars->Endpoint; // enter the endpoint number
if ((pars->Endpoint != 0) && (pars->Paired_RxEndpoint != 0)) { // this is a paired endpoint, where this endpoint is the receiver (OUT) in the paired channel
unsigned short usParameters = usb_endpoint_queue->usParameters;
usb_endpoint_queue = (usb_endpoint_control + pars->Paired_RxEndpoint);
usb_endpoint_queue->ucEndpointNumber = pars->Paired_RxEndpoint; // enter the endpoint number
usb_endpoint_queue->ucPaired_IN = pars->Endpoint; // {16}
usb_endpoint_queue->usMax_frame_length = pars->usEndpointSize; // enter endpoint size limit
usb_endpoint_queue->usParameters = usParameters; // copy the endpoint parameters to the paired endpoint
}
if ((usb_endpoint_queue->ptrEndpointOutCtr = (que_ids[DriverID - 1].input_buffer_control)) != 0) { // set to input control block
((USBQUE *)(usb_endpoint_queue->ptrEndpointOutCtr))->endpoint_control = usb_endpoint_queue;
usb_endpoint_queue->event_task = pars->owner_task;
}
usb_endpoint_queue->usb_callback = pars->usb_callback; // enter optional callback routine for endpoint (used by reception - OUT)
#if (defined USB_DMA_TX || defined USB_DMA_RX) && defined USB_RAM_START // {17}
if (NO_MEMORY == fnAllocateUSBBuffer(DriverID, usb_endpoint_queue, &pars->queue_sizes)) {
return NO_ID_ALLOCATED;
}
#endif
return (DriverID); // return the allocated ID (handle for the end point)
}
// Send a message to the endpoint owner task
//
static void fnUSB_message(unsigned char ucEvent, unsigned char *ptrData, unsigned char ucDataLength, UTASK_TASK destination)
{
#if defined USE_USB_AUDIO
unsigned char ucMessage[HEADER_LENGTH + 1 + 4]; // interface and alternative configuration as payload
#else
unsigned char ucMessage[HEADER_LENGTH + 1 + 1]; // single byte configuration as payload
#endif
if (destination == 0) {
return;
}
#if defined _WINDOWS
if (ucDataLength > (sizeof(ucMessage) - (HEADER_LENGTH + 1))) {
_EXCEPTION("Message queue length needs to be increased!!"); // we need to increase the message queue length above if this happens
}
#endif
uMemcpy(&ucMessage[HEADER_LENGTH + 1], ptrData, ucDataLength);
ucMessage[MSG_DESTINATION_NODE] = INTERNAL_ROUTE; // destination node
ucMessage[MSG_SOURCE_NODE] = INTERNAL_ROUTE; // own node
ucMessage[MSG_DESTINATION_TASK] = destination; // destination task
ucMessage[MSG_SOURCE_TASK] = TASK_USB; // own task (fictional)
ucMessage[MSG_CONTENT_LENGTH] = ++ucDataLength; // message length
ucMessage[MSG_CONTENT_COMMAND] = ucEvent; // event message type
fnWrite(INTERNAL_ROUTE, ucMessage, (QUEUE_TRANSFER)(ucDataLength + HEADER_LENGTH)); // send message to USB task
}
// Handle the lengths of individual FIFO buffers - the depth is hardware dependent
//
static void fnPushLastLength(USBQUE *endpoint_queue, unsigned short usLength)
{
if (endpoint_queue != 0) {
#if defined _USB_CATCH_ERROR
if (endpoint_queue->endpoint_control->ucFIFO_depth > 1) { // catch writing beyond end of FIFO length buffer
fnError(1);
}
#endif
endpoint_queue->endpoint_control->usLength[endpoint_queue->endpoint_control->ucFIFO_depth++] = usLength; // enter next
}
}
// Get the length of a previously transmitted buffer
//
static unsigned short fnPullLastLength(USB_ENDPOINT *endpoint_control)
{
unsigned short usLength = endpoint_control->usLength[0]; // length of last data token
#if USB_FIFO_BUFFER_DEPTH > 1
int iEntry = 1;
while (iEntry < endpoint_control->ucFIFO_depth) {
endpoint_control->usLength[iEntry - 1] = endpoint_control->usLength[iEntry]; // shift the lengths in the queue
iEntry++;
}
#endif
#if defined _USB_CATCH_ERROR
if (endpoint_control->ucFIFO_depth == 0) { // catch writing before start of FIFO length buffer
fnError(2);
}
#endif
endpoint_control->ucFIFO_depth--; // FIFO one less in depth
return (usLength);
}
// Get the total amount of data passed to USB buffers but not yet acknowledged
//
static unsigned short fnOutstandingData(USB_ENDPOINT *endpoint_control)
{
int iEntries = endpoint_control->ucFIFO_depth;
unsigned short usOutstanding = 0;
#if defined _USB_CATCH_ERROR
if (iEntries > 1) { // catch writing beyond end of FIFO length buffer
fnError(3);
}
#endif
while (iEntries-- != 0) {
usOutstanding += endpoint_control->usLength[iEntries]; // {10}
}
return usOutstanding;
}
// Request whether an endpoint is a control endpoint
//
static int fnControlEndpoint(int iEndpoint, unsigned char ucCheck) // {11}
{
/* {26}
if (iEndpoint == 0) {
return 1; // endpoint 0 is always a control endpoint
}
else {*/
USB_ENDPOINT *usb_endpoint_queue = (usb_endpoint_control + iEndpoint);
if (usb_endpoint_queue->ucState & ucCheck) { // this endpoint is configured as the check state
return 1;
}
//}
return 0;
}
// Enter the data to be transmitted into the management queue of its specific end point and start first packet transfer
//
#if defined USB_HOST_SUPPORT // {29}
static QUEUE_TRANSFER fnPrepareOutData(int iMode, unsigned char *ptrData, unsigned short usLength, unsigned short ucMaxLength, int iEndpoint, USB_HW *ptrUSB_HW)
#else
static QUEUE_TRANSFER fnPrepareOutData(unsigned char *ptrData, unsigned short usLength, unsigned short ucMaxLength, int iEndpoint, USB_HW *ptrUSB_HW)
#endif
{
#if defined USB_HOST_SUPPORT
unsigned char ucTransferType;
#endif
USB_ENDPOINT *tx_queue = (usb_endpoint_control + iEndpoint);
unsigned short usAdditionalLength; // {21}
if (tx_queue->usCompleteMessage != 0) { // transmission already in progress
return 0;
}
#if defined USB_HOST_SUPPORT // {29}
if (_USB_HOST_MODE()) {
if ((_HOST_SETUP == iMode) || ((iEndpoint == 0) && ((_DEVICE_DATA_HOST_SETUP == iMode)))) { // is host setup data is to be prepared
fnPrepareSetup(ptrUSB_HW); // prepare for setup frame transmission (this often requires the data token to be resysnchronised because the next transmitted data token is DATA0 and the next received data token is DATA1)
ucTransferType = SETUP_PID; // a SETUP token is to be sent
}
else {
ucTransferType = OUT_PID; // an OUT frame is to be sent
}
}
else {
iMode = _DEVICE_DATA; // not host mode
}
#endif
tx_queue->usLimitLength = ucMaxLength;
if (usLength > ucMaxLength) { // if host cannot accept full length, cut it shorter
usLength = ucMaxLength;
}
tx_queue->usCompleteMessage = usLength; // total queued length
#if defined USB_AUTO_TX // {25}
if (ptrUSB_HW->ucDeviceType == USB_DEVICE_HS) {
tx_queue->usSent = usLength; // always pass a single buffer which the USB HS controller sends in multiple frames
}
else {
#endif
#if defined MULTIPLE_TX // {22}
if (iEndpoint == 0) {
if (usLength > (MULTIPLE_TX*tx_queue->usMax_frame_length)) {
tx_queue->usSent = (MULTIPLE_TX*tx_queue->usMax_frame_length); // maximum endpoint length
}
else {
tx_queue->usSent = usLength; // complete message in one buffer
}
}
else {
if (usLength > tx_queue->usMax_frame_length) {
tx_queue->usSent = tx_queue->usMax_frame_length; // maximum endpoint length
}
else {
tx_queue->usSent = usLength; // complete message in one buffer
}
}
#else
if (usLength > tx_queue->usMax_frame_length) { // if the data length is greater than the endpoint size
tx_queue->usSent = tx_queue->usMax_frame_length; // set maximum endpoint length for first packet
}
else {
tx_queue->usSent = usLength; // complete message in one buffer
}
#endif
#if defined USB_AUTO_TX // {23}
}
#endif
usAdditionalLength = tx_queue->usSent;
FNSEND_USB_DATA(ptrData, usAdditionalLength, iEndpoint, ptrUSB_HW); // prepare packet in hardware buffer
fnPushLastLength((USBQUE *)(tx_queue->ptrEndpointInCtr), usAdditionalLength); // save last length for use later
tx_queue->ptrStart = ptrData; // set the start pointer to the start of the data packet
while (tx_queue->usCompleteMessage > tx_queue->usSent) { // controllers usually have multiple output buffers so fill up as many as possible
//unsigned short usAdditionalLength = tx_queue->usSent; // {21} set before entering the loop
USBQUE *endpoint_queue = (USBQUE *)(tx_queue->ptrEndpointInCtr); // {3} additional check of tx buffer depth
if (endpoint_queue != 0) { // if the endpoint is using a circular output buffer
if (endpoint_queue->endpoint_control->ucFIFO_depth >= USB_FIFO_BUFFER_DEPTH) { // {5} if all endpoint buffers are in use
//return tx_queue->usCompleteMessage;
break; // {6} all available buffers have been prepared
}
}
if (fnGetUSB_HW(iEndpoint, &ptrUSB_HW) != ENDPOINT_FREE) { // {28} get the next free buffer if possible
//return tx_queue->usCompleteMessage;
break; // {6} no further buffer ready to accet data
}
usLength -= usAdditionalLength; // remaining length to be queued
ptrData += usAdditionalLength;
if (usLength > tx_queue->usMax_frame_length) {
usAdditionalLength = tx_queue->usMax_frame_length; // maximum endpoint length
}
else {
usAdditionalLength = usLength; // complete message prepared
}
#if defined USB_TX_MESSAGE_MODE
if (tx_queue->messageQueue != 0) { // {32}
QUEQUE *ptTxQueue = &((USBQUE *)(tx_queue->ptrEndpointInCtr))->USB_queue;
if ((ptrData + usAdditionalLength) >= ptTxQueue->buffer_end) { // if the buffer doesn't fit in the circular buffer
ptrData = ptTxQueue->QUEbuffer; // move to start of circular buffer
tx_queue->ptrStart = (ptrData - tx_queue->usSent); // set virtual buffer start to compensate for the wrap-around
}
}
#endif
FNSEND_USB_DATA(ptrData, usAdditionalLength, iEndpoint, ptrUSB_HW); // prepare hardware buffer
fnPushLastLength((USBQUE *)(tx_queue->ptrEndpointInCtr), usAdditionalLength); // save last length for later use
tx_queue->usSent += usAdditionalLength; // size of present frame in progress
}
#if defined USB_HOST_SUPPORT // {29}
if (iMode > _DEVICE_DATA) { // if host data has been prepared
fnHostReleaseBuffer(iEndpoint, ucTransferType, ptrUSB_HW); // allow host to release prepared data
}
#endif
return tx_queue->usCompleteMessage; // message length that was accepted
}
#if defined USB_DEVICE_SUPPORT
// Extract (certain values) from standard descriptor requests in little-endian format
//
static void fnExtract(unsigned char *ptrData, unsigned char ucFlags, unsigned short *usValues)
{
#define VALUE_INDEX 0x01
#define INDEX_INDEX 0x02
#define LENGTH_INDEX 0x04
if (ucFlags & VALUE_INDEX) {
*usValues = GET_USB_DATA();
*usValues++ |= (GET_USB_DATA() << 8);
}
else {
#if defined USB_FIFO
GET_USB_DATA();GET_USB_DATA(); // {15}
#else
ptrData += sizeof(unsigned short);
#endif
}
if (ucFlags & INDEX_INDEX) {
*usValues = GET_USB_DATA();
*usValues++ |= (GET_USB_DATA() << 8);
}
else {
#if defined USB_FIFO
GET_USB_DATA();GET_USB_DATA(); // {15}
#else
ptrData += sizeof(unsigned short);
#endif
}
if (ucFlags & LENGTH_INDEX) {
*usValues = GET_USB_DATA();
*usValues |= (GET_USB_DATA() << 8);
}
}
#endif
#if defined WAKE_BLOCKED_USB_TX
// Wake a blocked USB IN endpoint queue by informing its owner task that there is space available to put new data to
//
static void fnWakeBlockedTx(USBQUE *ptrUsbQueue, QUEUE_TRANSFER low_water)
{
#if defined USB_SIMPLEX_ENDPOINTS || defined SUPPORT_USB_SIMPLEX_HOST_ENDPOINTS
if ((ptrUsbQueue->endpoint_control->event_task_in != 0) && (ptrUsbQueue->USB_queue.chars <= low_water))
#else
if ((ptrUsbQueue->endpoint_control->event_task != 0) && (ptrUsbQueue->USB_queue.chars <= low_water))
#endif
{ // we have just adeqately emptied buffer content so inform waiting transmitter task
unsigned char tx_continue_message[HEADER_LENGTH]; // = { INTERNAL_ROUTE, INTERNAL_ROUTE , ptrUsbQueue->tx_queue->event_task, INTERRUPT_EVENT, TX_FREE }; // define standard interrupt event
tx_continue_message[MSG_DESTINATION_NODE] = INTERNAL_ROUTE;
tx_continue_message[MSG_SOURCE_NODE] = INTERNAL_ROUTE;
#if defined USB_SIMPLEX_ENDPOINTS || defined SUPPORT_USB_SIMPLEX_HOST_ENDPOINTS
tx_continue_message[MSG_DESTINATION_TASK] = ptrUsbQueue->endpoint_control->event_task_in;
#else
tx_continue_message[MSG_DESTINATION_TASK] = ptrUsbQueue->endpoint_control->event_task;
#endif
tx_continue_message[MSG_SOURCE_TASK] = INTERRUPT_EVENT;
tx_continue_message[MSG_INTERRUPT_EVENT] = TX_FREE;
fnWrite(INTERNAL_ROUTE, (unsigned char*)tx_continue_message, HEADER_LENGTH); // inform the blocked task
#if defined USB_SIMPLEX_ENDPOINTS || defined SUPPORT_USB_SIMPLEX_HOST_ENDPOINTS
ptrUsbQueue->endpoint_control->event_task_in = 0; // remove task since this is only performed once
#else
ptrUsbQueue->endpoint_control->event_task = 0; // remove task since this is only performed once
#endif
}
}
#endif
#if defined USB_HOST_SUPPORT // {29}
#if defined USB_STRING_OPTION // requests strings if required
// Search for next string in the device descriptor (it is assumed that they count from 1..max but the order is not important)
//
static int fnNeedString(int iStringReference)
{
unsigned char *ptrStringRef = &device_descriptor.iManufacturer;
if (iStringReference == 0) { // language id is always valid
return 0;
}
while (ptrStringRef <= &device_descriptor.iSerialNumber) { // check manufacturer, product and serial number references
if (*ptrStringRef++ == (unsigned char)(iStringReference)) {
return 0;
}
}
return -1;
}
// Clears a stalled device endpoint by sending the ClearFeature
//
extern QUEUE_TRANSFER fnClearFeature(QUEUE_LIMIT control_handle, unsigned char ucEndpoint)
{
static unsigned char ucClearFeature[8]; // use static memory so that it remains stable when sending on non-buffered endpoint
ucStalledEndpoint = (IN_ENDPOINT | ucEndpoint); // note the endpoint that has stalled and will be cleared
ucClearFeature[0] = REQUEST_ENDPOINT_STANDARD; // 0x02 request type standard, recipient is endpoint, host to device
ucClearFeature[1] = USB_REQUEST_CLEAR_FEATURE; // 0x01 clear feature
ucClearFeature[2] = 0x00; // endpoint halt
ucClearFeature[3] = 0x00;
ucClearFeature[4] = ucStalledEndpoint; // IN endpoint and number
ucClearFeature[5] = 0x00;
ucClearFeature[6] = 0x00; // length 0
ucClearFeature[7] = 0x00;
return (fnWrite(control_handle, (unsigned char *)&ucClearFeature, 8)); // send clear feature on endpoint 0
}
#endif
// USB host enumeration controller
//
static int fnHostEmumeration(int iEndpoint, int iEvent, USB_HW *ptrUSB_HW)
{
static const USB_HOST_DESCRIPTOR get_device_descriptor = {
(STANDARD_DEVICE_TO_HOST), // 0x80 - recipient host, type standard, device-to-host
USB_REQUEST_GET_DESCRIPTOR, // 0x06 - get descriptor from device
{LITTLE_SHORT_WORD_BYTES(STANDARD_DEVICE_DESCRIPTOR)}, // standard device descriptor
{LITTLE_SHORT_WORD_BYTES(0)}, // no index
{LITTLE_SHORT_WORD_BYTES(sizeof(USB_DEVICE_DESCRIPTOR))} // size of response that is expected
};
static const USB_HOST_DESCRIPTOR set_address = {
(STANDARD_HOST_TO_DEVICE), // 0x00 - recipient device, type standard, host-to-device
USB_REQUEST_SET_ADDRESS, // 0x05 - set device's address
{LITTLE_SHORT_WORD_BYTES(USB_DEVICE_ADDRESS)}, // address
{LITTLE_SHORT_WORD_BYTES(0)}, // no index
{LITTLE_SHORT_WORD_BYTES(0)} // no response expected
};
static const USB_HOST_DESCRIPTOR get_configuration_descriptor = {
(STANDARD_DEVICE_TO_HOST), // 0x80 - recipient host, type standard, device-to-host
USB_REQUEST_GET_DESCRIPTOR, // 0x06 - get descriptor from device
{LITTLE_SHORT_WORD_BYTES(STANDARD_CONFIG_DESCRIPTOR)}, // standard device descriptor
{LITTLE_SHORT_WORD_BYTES(0)}, // no index
{LITTLE_SHORT_WORD_BYTES(255)} // size of response that is possible
};
USB_ENDPOINT *usb_endpoint_queue = usb_endpoint_control;
usb_endpoint_queue += iEndpoint;
if ((iEvent == HOST_APPLICATION_ON) && (ucRequestType != HOST_ENUMERATION_SET_CONFIGURATION)) {
return USB_HOST_ERROR; // only allow the application to set the configuration when in the required state
}
//if (DEVICE_TIMEOUT == iEvent) {
// ucRequestType--; // repeat last request due to no response
//}
switch (ucRequestType) {
case HOST_ENUMERATION_IDLE: // starting from device connection
#if defined USB_STRING_OPTION
iStringReference = 0;
#endif
ucRequestLengthRemaining = sizeof(USB_DEVICE_DESCRIPTOR);
fnPrepareOutData(HOST_SETUP (unsigned char *)&get_device_descriptor, sizeof(get_device_descriptor), 8, 0, ptrUSB_HW); // prepare setup stage of get device descriptor
fnInterruptMessage(usb_endpoint_control->event_task, (unsigned char)(EVENT_USB_DETECT_LS + ptrUSB_HW->ucDeviceSpeed)); // inform of the attachment and bus speed
ucRequestType = HOST_ENUMERATION_STANDARD_DEVICE_DESCRIPTOR; // note the request type that is in operation
break;
case HOST_ENUMERATION_STANDARD_DEVICE_DESCRIPTOR: // standard device descriptor has been received
ucRequestType = HOST_ENUMERATION_STANDARD_DEVICE_DESCRIPTOR_ACK; // set next state since we expect an ack
return TERMINATE_ZERO_DATA; // reception buffer has been consumed and we need to send a zero termination
case HOST_ENUMERATION_STANDARD_DEVICE_DESCRIPTOR_ACK:
fnPrepareOutData(HOST_SETUP (unsigned char *)&set_address, sizeof(set_address), 8, 0, ptrUSB_HW);
ucRequestType = HOST_ENUMERATION_SET_ADDRESS;
break;
case HOST_ENUMERATION_SET_ADDRESS: // address has been acknowledged by the device
fnSetUSB_device_address(USB_DEVICE_ADDRESS); // set the device address since it has been acknowledged
ucRequestLengthRemaining = 255; // up to 255 bytes possible
fnPrepareOutData(HOST_SETUP (unsigned char *)&get_configuration_descriptor, sizeof(get_configuration_descriptor), 8, 0, ptrUSB_HW); // send setup stage of get device descriptor (this is sent to the new address)
ucRequestType = HOST_ENUMERATION_CONFIGURATION_DESCRIPTOR; // set next state
break;
case HOST_ENUMERATION_CONFIGURATION_DESCRIPTOR:
ucConfigDescriptorLength = ucRequestingReceived; // the length of the received configuration descriptor
ucRequestType = HOST_ENUMERATION_CONFIGURATION_DESCRIPTOR_ACK; // set next state
return TERMINATE_ZERO_DATA; // reception buffer has been consumed and we need to send a zero termination
#if defined USB_STRING_OPTION
case HOST_ENUMERATION_REQUEST_STRING_ACK:
iStringReference++;
// Fall through intentional
//
#endif
case HOST_ENUMERATION_CONFIGURATION_DESCRIPTOR_ACK: // when we arrive here we have received the standard device descriptor and configuration descriptor and so have adequate information concerning the device attached to decide whether to work with it
#if defined USB_STRING_OPTION // requests strings if required
if ((iStringReference <= USB_MAX_STRINGS) && (fnNeedString(iStringReference) == 0)) { // get next string
USB_HOST_DESCRIPTOR get_string;
get_string.bmRecipient_device_direction = STANDARD_DEVICE_TO_HOST; // 0x80 - recipient host, type standard, device-to-host
get_string.bRequest = USB_REQUEST_GET_DESCRIPTOR; // 0x06 - get descriptor from device
get_string.wValue[0] = (unsigned char)iStringReference;
get_string.wValue[1] = DESCRIPTOR_TYPE_STRING; // 0x03
if (iStringReference == 0) { // if requesting string language
get_string.wIndex[0] = get_string.wIndex[1] = 0;
}
else {
get_string.wIndex[0] = ucStringList[0][2];
get_string.wIndex[1] = ucStringList[0][3];
}
get_string.wLength[0] = (unsigned char)((USB_MAX_STRING_LENGTH * 2) + 1); // length and unicode string
get_string.wLength[1] = 0;
ucRequestLengthRemaining = (unsigned char)(USB_MAX_STRING_LENGTH * 2); // up to maximum string length
fnPrepareOutData(HOST_SETUP (unsigned char *)&get_string, sizeof(get_string), 8, 0, ptrUSB_HW); // send setup stage of get device descriptor
ucRequestType = HOST_ENUMERATION_REQUEST_STRING;
break;
}
#endif
fnUSB_message(E_USB_DEVICE_INFO, 0, 0, usb_endpoint_queue->event_task); // inform the USB application that this information is ready
ucRequestType = HOST_ENUMERATION_SET_CONFIGURATION; // enumeration information has been collected - the application will decide whether a configuration is set
break;
#if defined USB_STRING_OPTION
case HOST_ENUMERATION_REQUEST_STRING:
ucRequestingReceived = 0; // {34} reset previous string reception length counter since this transaction has completed
ucRequestType = HOST_ENUMERATION_REQUEST_STRING_ACK; // set next state since we expect an ack
return TERMINATE_ZERO_DATA; // reception buffer has been consumed and we need to send a zero termination
#endif
case HOST_ENUMERATION_SET_CONFIGURATION: // the configuration is to be set
{
static USB_HOST_DESCRIPTOR set_configuration;
USB_HW *ptrUSB_HW; // pointer to be set to the present host hardware information
if (fnGetUSB_HW(0, &ptrUSB_HW) != ENDPOINT_FREE) { // {28} get details about the transmitter hardware (endpoint 0)
return USB_HOST_ERROR; // hardware must never be blocked
}
uMemset(&set_configuration, 0, sizeof(set_configuration));
set_configuration.bmRecipient_device_direction = STANDARD_HOST_TO_DEVICE; // 0x00 - recipient device, type standard, host-to-device
set_configuration.bRequest = USB_REQUEST_SET_CONFIGURATION; // 0x09 - set configuration
set_configuration.wValue[0] = ucActiveConfiguration;
fnPrepareOutData(HOST_SETUP (unsigned char *)&set_configuration, sizeof(set_configuration), 8, 0, ptrUSB_HW); // send setup stage of set configuration
ucRequestType = HOST_ENUMERATION_SET_CONFIGURATION_ACK;
break;
}
break;
case HOST_ENUMERATION_SET_CONFIGURATION_ACK:
#if defined SET_INTERFACE
{
static const USB_HOST_DESCRIPTOR set_interface = {
(STANDARD_HOST_TO_DEVICE | REQUEST_INTERFACE_STANDARD), // 0x01 request class specific interface
USB_REQUEST_SET_INTERFACE, // 0x0b
{LITTLE_SHORT_WORD_BYTES(0)},
{LITTLE_SHORT_WORD_BYTES(0)},
{LITTLE_SHORT_WORD_BYTES(0)},
};
fnPrepareOutData(HOST_SETUP (unsigned char *)&set_interface, sizeof(set_interface), 8, 0, ptrUSB_HW); // send setup stage of set configuration
ucRequestType = HOST_ENUMERATION_SET_INTERFACE_ACK;
}
break;
case HOST_ENUMERATION_SET_INTERFACE_ACK:
#endif
ucRequestType = HOST_ENUMERATION_CONFIGURED; // the device has been configured and the host is fully operating
fnUSB_message(E_USB_ACTIVATE_CONFIGURATION, &ucActiveConfiguration, sizeof(ucActiveConfiguration), usb_endpoint_control->event_task); // enumeration has completed
break;
case HOST_ENUMERATION_CONFIGURED:
ucRequestType = HOST_ENUMERATION_CONFIGURED_ACK; // we will send a zero data frame once the device has responded
return TERMINATE_ZERO_DATA; // reception buffer has been consumed and we need to send a zero termination
case HOST_ENUMERATION_CONFIGURED_ACK:
//ucRequestType = HOST_ENUMERATION_CONFIGURED; // return to configured state
ucRequestType = HOST_ENUMERATION_CONFIGURED_IDLE;
if (usb_endpoint_queue->usb_callback != 0) {
return (usb_endpoint_queue->usb_callback(0, 0, STATUS_STAGE_RECEPTION)); // inform the user of the fact that the transaction was completed successfully (the user can also prepare new control data)
}
break;
case HOST_ENUMERATION_CONFIGURED_IDLE:
if (ucStalledEndpoint != 0) { // acknowledge from unstalling endpoint