Skip to content

Commit 10432a3

Browse files
committed
Porting CDC to PluggableUSB
1 parent 5c562e1 commit 10432a3

File tree

10 files changed

+283
-265
lines changed

10 files changed

+283
-265
lines changed

cores/arduino/Arduino.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -130,4 +130,8 @@ void loop( void ) ;
130130
#include "USB/USBAPI.h"
131131
#include "USB/USB_host.h"
132132

133+
#ifdef __cplusplus
134+
#include "USB/CDC.h"
135+
#endif
136+
133137
#endif // Arduino_h

cores/arduino/USB/CDC.cpp

Lines changed: 94 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -18,13 +18,17 @@
1818

1919
#include <Arduino.h>
2020
#include <Reset.h> // Needed for auto-reset with 1200bps port touch
21+
#include "CDC.h"
22+
#include "SAMD21_USBDevice.h"
2123

2224
#include <stdlib.h>
2325
#include <stdio.h>
2426
#include <stdint.h>
2527

2628
#ifdef CDC_ENABLED
2729

30+
extern USBDevice_SAMD21G18x usbd;
31+
2832
#define CDC_SERIAL_BUFFER_SIZE 256
2933

3034
/* For information purpose only since RTS is not always handled by the terminal application */
@@ -51,46 +55,94 @@ static volatile LineInfo _usbLineInfo = {
5155
};
5256

5357
static volatile int32_t breakValue = -1;
58+
_Pragma("pack()")
5459

55-
static CDCDescriptor _cdcInterface = {
56-
D_IAD(0, 2, CDC_COMMUNICATION_INTERFACE_CLASS, CDC_ABSTRACT_CONTROL_MODEL, 0),
60+
// CDC
61+
#define CDC_ACM_INTERFACE pluggedInterface // CDC ACM
62+
#define CDC_DATA_INTERFACE pluggedInterface+1 // CDC Data
63+
#define CDC_ENDPOINT_ACM pluggedEndpoint
64+
#define CDC_ENDPOINT_OUT pluggedEndpoint+1
65+
#define CDC_ENDPOINT_IN pluggedEndpoint+2
5766

58-
// CDC communication interface
59-
D_INTERFACE(CDC_ACM_INTERFACE, 1, CDC_COMMUNICATION_INTERFACE_CLASS, CDC_ABSTRACT_CONTROL_MODEL, 0),
60-
D_CDCCS(CDC_HEADER, CDC_V1_10 & 0xFF, (CDC_V1_10>>8) & 0x0FF), // Header (1.10 bcd)
67+
#define CDC_RX CDC_ENDPOINT_OUT
68+
#define CDC_TX CDC_ENDPOINT_IN
69+
70+
int Serial_::getInterface(uint8_t* interfaceNum)
71+
{
72+
interfaceNum[0] += 2; // uses 2
73+
CDCDescriptor _cdcInterface = {
74+
D_IAD(pluggedInterface, 2, CDC_COMMUNICATION_INTERFACE_CLASS, CDC_ABSTRACT_CONTROL_MODEL, 0),
6175

62-
D_CDCCS4(CDC_ABSTRACT_CONTROL_MANAGEMENT, 6), // SET_LINE_CODING, GET_LINE_CODING, SET_CONTROL_LINE_STATE supported
63-
D_CDCCS(CDC_UNION, CDC_ACM_INTERFACE, CDC_DATA_INTERFACE), // Communication interface is master, data interface is slave 0
64-
D_CDCCS(CDC_CALL_MANAGEMENT, 1, 1), // Device handles call management (not)
65-
D_ENDPOINT(USB_ENDPOINT_IN(CDC_ENDPOINT_ACM), USB_ENDPOINT_TYPE_INTERRUPT, 0x10, 0x10),
76+
// CDC communication interface
77+
D_INTERFACE(CDC_ACM_INTERFACE, 1, CDC_COMMUNICATION_INTERFACE_CLASS, CDC_ABSTRACT_CONTROL_MODEL, 0),
78+
D_CDCCS(CDC_HEADER, CDC_V1_10 & 0xFF, (CDC_V1_10>>8) & 0x0FF), // Header (1.10 bcd)
6679

67-
// CDC data interface
68-
D_INTERFACE(CDC_DATA_INTERFACE, 2, CDC_DATA_INTERFACE_CLASS, 0, 0),
69-
D_ENDPOINT(USB_ENDPOINT_OUT(CDC_ENDPOINT_OUT), USB_ENDPOINT_TYPE_BULK, EPX_SIZE, 0),
70-
D_ENDPOINT(USB_ENDPOINT_IN (CDC_ENDPOINT_IN ), USB_ENDPOINT_TYPE_BULK, EPX_SIZE, 0)
71-
};
72-
_Pragma("pack()")
80+
D_CDCCS4(CDC_ABSTRACT_CONTROL_MANAGEMENT, 6), // SET_LINE_CODING, GET_LINE_CODING, SET_CONTROL_LINE_STATE supported
81+
D_CDCCS(CDC_UNION, CDC_ACM_INTERFACE, CDC_DATA_INTERFACE), // Communication interface is master, data interface is slave 0
82+
D_CDCCS(CDC_CALL_MANAGEMENT, 1, 1), // Device handles call management (not)
83+
D_ENDPOINT(USB_ENDPOINT_IN(CDC_ENDPOINT_ACM), USB_ENDPOINT_TYPE_INTERRUPT, 0x10, 0x10),
7384

74-
const void* _CDC_GetInterface(void)
75-
{
76-
return &_cdcInterface;
85+
// CDC data interface
86+
D_INTERFACE(CDC_DATA_INTERFACE, 2, CDC_DATA_INTERFACE_CLASS, 0, 0),
87+
D_ENDPOINT(USB_ENDPOINT_OUT(CDC_ENDPOINT_OUT), USB_ENDPOINT_TYPE_BULK, EPX_SIZE, 0),
88+
D_ENDPOINT(USB_ENDPOINT_IN (CDC_ENDPOINT_IN), USB_ENDPOINT_TYPE_BULK, EPX_SIZE, 0)
89+
};
90+
91+
return USBDevice.sendControl(&_cdcInterface, sizeof(_cdcInterface));
7792
}
7893

79-
uint32_t _CDC_GetInterfaceLength(void)
94+
int Serial_::getDescriptor(USBSetup& setup)
8095
{
81-
return sizeof(_cdcInterface);
96+
return 0;
8297
}
8398

84-
int CDC_GetInterface(uint8_t* interfaceNum)
85-
{
86-
interfaceNum[0] += 2; // uses 2
87-
return USBDevice.sendControl(&_cdcInterface,sizeof(_cdcInterface));
99+
static void utox8(uint32_t val, char* s) {
100+
for (int i = 0; i < 8; i++) {
101+
int d = val & 0XF;
102+
val = (val >> 4);
103+
104+
s[7 - i] = d > 9 ? 'A' + d - 10 : '0' + d;
105+
}
106+
}
107+
108+
uint8_t Serial_::getShortName(char* name) {
109+
// from section 9.3.3 of the datasheet
110+
#define SERIAL_NUMBER_WORD_0 *(volatile uint32_t*)(0x0080A00C)
111+
#define SERIAL_NUMBER_WORD_1 *(volatile uint32_t*)(0x0080A040)
112+
#define SERIAL_NUMBER_WORD_2 *(volatile uint32_t*)(0x0080A044)
113+
#define SERIAL_NUMBER_WORD_3 *(volatile uint32_t*)(0x0080A048)
114+
115+
utox8(SERIAL_NUMBER_WORD_0, &name[0]);
116+
utox8(SERIAL_NUMBER_WORD_1, &name[8]);
117+
utox8(SERIAL_NUMBER_WORD_2, &name[16]);
118+
utox8(SERIAL_NUMBER_WORD_3, &name[24]);
119+
return 32;
88120
}
89121

90-
bool CDC_Setup(USBSetup& setup)
122+
void Serial_::handleEndpoint(int ep) {
123+
if (ep == CDC_ENDPOINT_IN)
124+
{
125+
// NAK on endpoint IN, the bank is not yet filled in.
126+
usbd.epBank1ResetReady(CDC_ENDPOINT_IN);
127+
usbd.epBank1AckTransferComplete(CDC_ENDPOINT_IN);
128+
}
129+
if (ep == CDC_ENDPOINT_ACM)
130+
{
131+
// NAK on endpoint IN, the bank is not yet filled in.
132+
usbd.epBank1ResetReady(CDC_ENDPOINT_ACM);
133+
usbd.epBank1AckTransferComplete(CDC_ENDPOINT_ACM);
134+
}
135+
}
136+
137+
bool Serial_::setup(USBSetup& setup)
91138
{
92139
uint8_t requestType = setup.bmRequestType;
93140
uint8_t r = setup.bRequest;
141+
uint8_t i = setup.wIndex;
142+
143+
if (CDC_ACM_INTERFACE != i) {
144+
return false;
145+
}
94146

95147
if (requestType == REQUEST_DEVICETOHOST_CLASS_INTERFACE)
96148
{
@@ -103,6 +155,7 @@ bool CDC_Setup(USBSetup& setup)
103155

104156
if (requestType == REQUEST_HOSTTODEVICE_CLASS_INTERFACE)
105157
{
158+
106159
if (r == CDC_SET_LINE_CODING)
107160
{
108161
USBDevice.recvControl((void*)&_usbLineInfo, 7);
@@ -126,18 +179,32 @@ bool CDC_Setup(USBSetup& setup)
126179
{
127180
cancelReset();
128181
}
129-
return false;
182+
USBDevice.sendZlp(0);
130183
}
131184

132185
if (CDC_SEND_BREAK == r)
133186
{
134187
breakValue = ((uint16_t)setup.wValueH << 8) | setup.wValueL;
135-
return false;
188+
USBDevice.sendZlp(0);
136189
}
190+
return true;
137191
}
138192
return false;
139193
}
140194

195+
Serial_::Serial_(USBDeviceClass &_usb) : PluggableUSBModule(3, 2, epType), usb(_usb), stalled(false)
196+
{
197+
epType[0] = USB_ENDPOINT_TYPE_INTERRUPT | USB_ENDPOINT_IN(0);
198+
epType[1] = USB_ENDPOINT_TYPE_BULK | USB_ENDPOINT_OUT(0);
199+
epType[2] = USB_ENDPOINT_TYPE_BULK | USB_ENDPOINT_IN(0);
200+
PluggableUSB().plug(this);
201+
}
202+
203+
void Serial_::enableInterrupt() {
204+
usbd.epBank1EnableTransferComplete(CDC_ENDPOINT_ACM);
205+
usbd.epBank0EnableTransferComplete(CDC_ENDPOINT_OUT);
206+
}
207+
141208
void Serial_::begin(uint32_t /* baud_count */)
142209
{
143210
// uart config is ignored in USB-CDC

cores/arduino/USB/CDC.h

Lines changed: 161 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,161 @@
1+
#ifndef __CDC_H__
2+
#define __CDC_H__
3+
4+
#ifdef CDC_ENABLED
5+
6+
#include "USBDesc.h"
7+
#include "USBAPI.h"
8+
#include "PluggableUSB.h"
9+
10+
11+
#define CDC_V1_10 0x0110
12+
#define CDC_COMMUNICATION_INTERFACE_CLASS 0x02
13+
14+
#define CDC_CALL_MANAGEMENT 0x01
15+
#define CDC_ABSTRACT_CONTROL_MODEL 0x02
16+
#define CDC_HEADER 0x00
17+
#define CDC_ABSTRACT_CONTROL_MANAGEMENT 0x02
18+
#define CDC_UNION 0x06
19+
#define CDC_CS_INTERFACE 0x24
20+
#define CDC_CS_ENDPOINT 0x25
21+
#define CDC_DATA_INTERFACE_CLASS 0x0A
22+
23+
// CDC CS interface descriptor
24+
typedef struct
25+
{
26+
uint8_t len; // 5
27+
uint8_t dtype; // 0x24
28+
uint8_t subtype;
29+
uint8_t d0;
30+
uint8_t d1;
31+
} CDCCSInterfaceDescriptor;
32+
33+
typedef struct
34+
{
35+
uint8_t len; // 4
36+
uint8_t dtype; // 0x24
37+
uint8_t subtype;
38+
uint8_t d0;
39+
} CDCCSInterfaceDescriptor4;
40+
41+
typedef struct
42+
{
43+
uint8_t len;
44+
uint8_t dtype; // 0x24
45+
uint8_t subtype; // 1
46+
uint8_t bmCapabilities;
47+
uint8_t bDataInterface;
48+
} CMFunctionalDescriptor;
49+
50+
typedef struct
51+
{
52+
uint8_t len;
53+
uint8_t dtype; // 0x24
54+
uint8_t subtype; // 1
55+
uint8_t bmCapabilities;
56+
} ACMFunctionalDescriptor;
57+
58+
typedef struct
59+
{
60+
// IAD
61+
IADDescriptor iad; // Only needed on compound device
62+
// Control
63+
InterfaceDescriptor cif;
64+
CDCCSInterfaceDescriptor header;
65+
ACMFunctionalDescriptor controlManagement; // ACM
66+
CDCCSInterfaceDescriptor functionalDescriptor; // CDC_UNION
67+
CMFunctionalDescriptor callManagement; // Call Management
68+
EndpointDescriptor cifin;
69+
70+
// Data
71+
InterfaceDescriptor dif;
72+
EndpointDescriptor in;
73+
EndpointDescriptor out;
74+
} CDCDescriptor;
75+
76+
77+
//================================================================================
78+
// Serial over CDC (Serial1 is the physical port)
79+
80+
class Serial_ : public Stream, public PluggableUSBModule {
81+
public:
82+
Serial_(USBDeviceClass &_usb);
83+
84+
void begin(uint32_t baud_count);
85+
void begin(unsigned long, uint8_t);
86+
void end(void);
87+
88+
virtual int available(void);
89+
virtual int availableForWrite(void);
90+
virtual int peek(void);
91+
virtual int read(void);
92+
virtual void flush(void);
93+
virtual size_t write(uint8_t);
94+
virtual size_t write(const uint8_t *buffer, size_t size);
95+
using Print::write; // pull in write(str) from Print
96+
operator bool();
97+
98+
size_t readBytes(char *buffer, size_t length);
99+
100+
// This method allows processing "SEND_BREAK" requests sent by
101+
// the USB host. Those requests indicate that the host wants to
102+
// send a BREAK signal and are accompanied by a single uint16_t
103+
// value, specifying the duration of the break. The value 0
104+
// means to end any current break, while the value 0xffff means
105+
// to start an indefinite break.
106+
// readBreak() will return the value of the most recent break
107+
// request, but will return it at most once, returning -1 when
108+
// readBreak() is called again (until another break request is
109+
// received, which is again returned once).
110+
// This also mean that if two break requests are received
111+
// without readBreak() being called in between, the value of the
112+
// first request is lost.
113+
// Note that the value returned is a long, so it can return
114+
// 0-0xffff as well as -1.
115+
int32_t readBreak();
116+
117+
// These return the settings specified by the USB host for the
118+
// serial port. These aren't really used, but are offered here
119+
// in case a sketch wants to act on these settings.
120+
uint32_t baud();
121+
uint8_t stopbits();
122+
uint8_t paritytype();
123+
uint8_t numbits();
124+
bool dtr();
125+
bool rts();
126+
enum {
127+
ONE_STOP_BIT = 0,
128+
ONE_AND_HALF_STOP_BIT = 1,
129+
TWO_STOP_BITS = 2,
130+
};
131+
enum {
132+
NO_PARITY = 0,
133+
ODD_PARITY = 1,
134+
EVEN_PARITY = 2,
135+
MARK_PARITY = 3,
136+
SPACE_PARITY = 4,
137+
};
138+
139+
protected:
140+
// Implementation of the PUSBListNode
141+
int getInterface(uint8_t* interfaceNum);
142+
int getDescriptor(USBSetup& setup);
143+
bool setup(USBSetup& setup);
144+
uint8_t getShortName(char* name);
145+
void handleEndpoint(int ep);
146+
void enableInterrupt();
147+
148+
friend USBDeviceClass;
149+
150+
private:
151+
int availableForStore(void);
152+
153+
USBDeviceClass &usb;
154+
RingBuffer *_cdc_rx_buffer;
155+
bool stalled;
156+
uint32_t epType[3];
157+
};
158+
extern Serial_ SerialUSB;
159+
160+
#endif
161+
#endif

cores/arduino/USB/PluggableUSB.cpp

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -76,6 +76,14 @@ bool PluggableUSB_::setup(USBSetup& setup)
7676
return false;
7777
}
7878

79+
void PluggableUSB_::handleEndpoint(int ep)
80+
{
81+
PluggableUSBModule* node;
82+
for (node = rootNode; node; node = node->next) {
83+
node->handleEndpoint(ep);
84+
}
85+
}
86+
7987
bool PluggableUSB_::plug(PluggableUSBModule *node)
8088
{
8189
if ((lastEp + node->numEndpoints) > USB_ENDPOINTS) {
@@ -109,8 +117,8 @@ PluggableUSB_& PluggableUSB()
109117
return obj;
110118
}
111119

112-
PluggableUSB_::PluggableUSB_() : lastIf(CDC_ACM_INTERFACE + CDC_INTERFACE_COUNT),
113-
lastEp(CDC_FIRST_ENDPOINT + CDC_ENPOINT_COUNT),
120+
PluggableUSB_::PluggableUSB_() : lastIf(0),
121+
lastEp(1),
114122
rootNode(NULL)
115123
{
116124
// Empty

cores/arduino/USB/PluggableUSB.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@ class PluggableUSBModule {
3535
virtual bool setup(USBSetup& setup) = 0;
3636
virtual int getInterface(uint8_t* interfaceCount) = 0;
3737
virtual int getDescriptor(USBSetup& setup) = 0;
38+
virtual void handleEndpoint(int ep) {/* Do nothing */}
3839
virtual uint8_t getShortName(char *name) { name[0] = 'A'+pluggedInterface; return 1; }
3940

4041
uint8_t pluggedInterface;
@@ -56,6 +57,7 @@ class PluggableUSB_ {
5657
int getInterface(uint8_t* interfaceCount);
5758
int getDescriptor(USBSetup& setup);
5859
bool setup(USBSetup& setup);
60+
void handleEndpoint(int ep);
5961
uint8_t getShortName(char *iSerialNum);
6062

6163
private:

0 commit comments

Comments
 (0)