Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

CDC Notification Endpoint, so we can send CTS/DSR/DCD/RI. #493

Draft
wants to merge 3 commits into
base: master
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
44 changes: 44 additions & 0 deletions src/arduino/Adafruit_USBD_CDC.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -170,6 +170,50 @@ int Adafruit_USBD_CDC::dtr(void) {
return tud_cdc_n_connected(_instance);
}

bool Adafruit_USBD_CDC::rts(void) {
return tud_cdc_n_get_line_state(_instance) & CDC_CONTROL_LINE_STATE_RTS;
}

bool Adafruit_USBD_CDC::cts(void) {
return tud_cdc_n_get_serial_state(_instance).cts;
}

bool Adafruit_USBD_CDC::dsr(void) {
return tud_cdc_n_get_serial_state(_instance).dsr;
}

bool Adafruit_USBD_CDC::dcd(void) {
return tud_cdc_n_get_serial_state(_instance).dcd;
}

bool Adafruit_USBD_CDC::ri(void) {
return tud_cdc_n_get_serial_state(_instance).ri;
}

void Adafruit_USBD_CDC::cts(bool c) {
cdc_serial_state_t serial_state = tud_cdc_n_get_serial_state(_instance);
serial_state.cts = c;
tud_cdc_n_set_serial_state(_instance, serial_state);
}

void Adafruit_USBD_CDC::dsr(bool c) {
cdc_serial_state_t serial_state = tud_cdc_n_get_serial_state(_instance);
serial_state.dsr = c;
tud_cdc_n_set_serial_state(_instance, serial_state);
}

void Adafruit_USBD_CDC::dcd(bool c) {
cdc_serial_state_t serial_state = tud_cdc_n_get_serial_state(_instance);
serial_state.dcd = c;
tud_cdc_n_set_serial_state(_instance, serial_state);
}

void Adafruit_USBD_CDC::ri(bool c) {
cdc_serial_state_t serial_state = tud_cdc_n_get_serial_state(_instance);
serial_state.ri = c;
tud_cdc_n_set_serial_state(_instance, serial_state);
}

Adafruit_USBD_CDC::operator bool() {
if (!isValid()) {
return false;
Expand Down
15 changes: 14 additions & 1 deletion src/arduino/Adafruit_USBD_CDC.h
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,20 @@ class Adafruit_USBD_CDC : public Stream, public Adafruit_USBD_Interface {
uint8_t stopbits(void);
uint8_t paritytype(void);
uint8_t numbits(void);
int dtr(void);

// Flow control bit getters.
int dtr(void); // pre-existing, I don't want to change the return type.
bool rts(void);
bool cts(void);
bool dsr(void);
bool dcd(void);
bool ri(void);

// Flow control bit setters.
void cts(bool c);
void dsr(bool c);
void dcd(bool c);
void ri(bool c);

// Stream API
virtual int available(void);
Expand Down
42 changes: 42 additions & 0 deletions src/class/cdc/cdc.h
Original file line number Diff line number Diff line change
Expand Up @@ -412,6 +412,48 @@ typedef struct TU_ATTR_PACKED

TU_VERIFY_STATIC(sizeof(cdc_line_control_state_t) == 2, "size is not correct");

//--------------------------------------------------------------------+
// Notifications
//--------------------------------------------------------------------+

typedef struct TU_ATTR_PACKED {
uint16_t dcd :1;
uint16_t dsr :1;
uint16_t break_err :1;
uint16_t ri :1;
uint16_t frame_err :1;
uint16_t parity_err :1;
uint16_t overrun_err :1;
uint16_t cts :1; // https://community.st.com/t5/stm32-mcus-products/cts-signal-on-usb-cdc/td-p/325800
uint16_t :8;
} cdc_serial_state_t;

TU_VERIFY_STATIC(sizeof(cdc_serial_state_t) == 2, "size is not correct");

// Add more notifications here. PSTN120.pdf, Section 6.5
typedef struct TU_ATTR_PACKED {
tusb_notification_t header;
union {
cdc_serial_state_t serial_state;
// Add more Notification "Data" payloads here as more are implemented.
};
} cdc_notify_struct_t;

TU_VERIFY_STATIC(sizeof(cdc_notify_struct_t) == 10, "size is not correct");

tu_static const cdc_notify_struct_t cdc_notify_serial_status = {
.header = {
.bmRequestType_bit = {
.recipient = TUSB_REQ_RCPT_INTERFACE,
.type = TUSB_REQ_TYPE_CLASS,
.direction = TUSB_DIR_IN,
},
.bNotification = CDC_NOTIF_SERIAL_STATE,
.wValue = 0,
.wLength = sizeof(cdc_serial_state_t),
}
};

TU_ATTR_PACKED_END // End of all packed definitions
TU_ATTR_BIT_FIELD_ORDER_END

Expand Down
55 changes: 50 additions & 5 deletions src/class/cdc/cdc_device.c
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,10 @@ typedef struct {
// Bit 0: DTR (Data Terminal Ready), Bit 1: RTS (Request to Send)
uint8_t line_state;

// Notify host of flow control bits: CTS, DSR, DCD, RI, and some error flags.
cdc_serial_state_t serial_state;
bool serial_state_changed;

/*------------- From this point, data is not cleared by bus reset -------------*/
char wanted_char;
TU_ATTR_ALIGNED(4) cdc_line_coding_t line_coding;
Expand All @@ -76,6 +80,7 @@ typedef struct {
// Endpoint Transfer buffer
CFG_TUSB_MEM_ALIGN uint8_t epout_buf[CFG_TUD_CDC_EP_BUFSIZE];
CFG_TUSB_MEM_ALIGN uint8_t epin_buf[CFG_TUD_CDC_EP_BUFSIZE];
CFG_TUSB_MEM_ALIGN cdc_notify_struct_t epnotif_buf;
} cdcd_interface_t;

#define ITF_MEM_RESET_SIZE offsetof(cdcd_interface_t, wanted_char)
Expand Down Expand Up @@ -138,6 +143,18 @@ uint8_t tud_cdc_n_get_line_state(uint8_t itf) {
return _cdcd_itf[itf].line_state;
}

cdc_serial_state_t tud_cdc_n_get_serial_state(uint8_t itf) {
return _cdcd_itf[itf].serial_state;
}

void tud_cdc_n_set_serial_state(uint8_t itf, cdc_serial_state_t serial_state) {
if (memcmp(&(_cdcd_itf[itf].serial_state), &serial_state, sizeof(serial_state)) != 0) {
TU_LOG_DRV(" Serial State Changed: %x -> %x\r\n", _cdcd_itf[itf].serial_state, serial_state);
_cdcd_itf[itf].serial_state_changed = true;
}
_cdcd_itf[itf].serial_state = serial_state;
}

void tud_cdc_n_get_line_coding(uint8_t itf, cdc_line_coding_t* coding) {
(*coding) = _cdcd_itf[itf].line_coding;
}
Expand Down Expand Up @@ -326,10 +343,13 @@ uint16_t cdcd_open(uint8_t rhport, tusb_desc_interface_t const * itf_desc, uint1

if (TUSB_DESC_ENDPOINT == tu_desc_type(p_desc)) {
// notification endpoint
tusb_desc_endpoint_t const* desc_ep = (tusb_desc_endpoint_t const*) p_desc;
tusb_desc_endpoint_t desc_ep = *(tusb_desc_endpoint_t const*) p_desc;
TU_LOG_DRV(" Interval before: %d\r\n", desc_ep.bInterval);

desc_ep.bInterval = 1; // Query every frame, 1ms at Full Speed.

TU_ASSERT(usbd_edpt_open(rhport, desc_ep), 0);
p_cdc->ep_notif = desc_ep->bEndpointAddress;
TU_ASSERT(usbd_edpt_open(rhport, &desc_ep), 0);
p_cdc->ep_notif = desc_ep.bEndpointAddress;

drv_len += tu_desc_len(p_desc);
p_desc = tu_desc_next(p_desc);
Expand Down Expand Up @@ -437,12 +457,13 @@ bool cdcd_xfer_cb(uint8_t rhport, uint8_t ep_addr, xfer_result_t result, uint32_
// Identify which interface to use
for (itf = 0; itf < CFG_TUD_CDC; itf++) {
p_cdc = &_cdcd_itf[itf];
if ((ep_addr == p_cdc->ep_out) || (ep_addr == p_cdc->ep_in)) break;
if ((ep_addr == p_cdc->ep_out) || (ep_addr == p_cdc->ep_in) || (ep_addr == p_cdc->ep_notif)) break;
}
TU_ASSERT(itf < CFG_TUD_CDC);

// Received new data
if (ep_addr == p_cdc->ep_out) {
TU_LOG_DRV(" XFer Out\r\n");
tu_fifo_write_n(&p_cdc->rx_ff, p_cdc->epout_buf, (uint16_t) xferred_bytes);

// Check for wanted char and invoke callback if needed
Expand All @@ -465,6 +486,7 @@ bool cdcd_xfer_cb(uint8_t rhport, uint8_t ep_addr, xfer_result_t result, uint32_
// Note: This will cause incorrect baudrate set in line coding.
// Though maybe the baudrate is not really important !!!
if (ep_addr == p_cdc->ep_in) {
TU_LOG_DRV(" XFer In\r\n");
// invoke transmit callback to possibly refill tx fifo
if (tud_cdc_tx_complete_cb) tud_cdc_tx_complete_cb(itf);

Expand All @@ -479,7 +501,30 @@ bool cdcd_xfer_cb(uint8_t rhport, uint8_t ep_addr, xfer_result_t result, uint32_
}
}

// nothing to do with notif endpoint for now
// Notifications
if (ep_addr == p_cdc->ep_notif) {
TU_LOG_DRV(" XFer Notification\r\n");
uint8_t const rhport = 0;

// SERIAL_STATE notification. Send flow control signals.
if (p_cdc->serial_state_changed) {
p_cdc->serial_state_changed = false;

// Build the notification
p_cdc->epnotif_buf = cdc_notify_serial_status;
p_cdc->epnotif_buf.header.wIndex = p_cdc->itf_num;
p_cdc->epnotif_buf.serial_state = p_cdc->serial_state;

// claim endpoint
TU_VERIFY(usbd_edpt_claim(rhport, p_cdc->ep_notif), 0);

// Send notification
return usbd_edpt_xfer(rhport, p_cdc->ep_notif, (uint8_t *) &(p_cdc->epnotif_buf), sizeof(p_cdc->epnotif_buf));
}
else {
// Send a NAK?
}
}

return true;
}
Expand Down
14 changes: 14 additions & 0 deletions src/class/cdc/cdc_device.h
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,12 @@ bool tud_cdc_n_connected(uint8_t itf);
// Get current line state. Bit 0: DTR (Data Terminal Ready), Bit 1: RTS (Request to Send)
uint8_t tud_cdc_n_get_line_state(uint8_t itf);

// Get current serial state.
cdc_serial_state_t tud_cdc_n_get_serial_state(uint8_t itf);

// Set current serial state.
void tud_cdc_n_set_serial_state(uint8_t itf, cdc_serial_state_t ser_state);

// Get current line encoding: bit rate, stop bits parity etc ..
void tud_cdc_n_get_line_coding(uint8_t itf, cdc_line_coding_t* coding);

Expand Down Expand Up @@ -132,6 +138,14 @@ TU_ATTR_ALWAYS_INLINE static inline uint8_t tud_cdc_get_line_state(void) {
return tud_cdc_n_get_line_state(0);
}

TU_ATTR_ALWAYS_INLINE static inline cdc_serial_state_t tud_cdc_get_serial_state(void) {
return tud_cdc_n_get_serial_state(0);
}

TU_ATTR_ALWAYS_INLINE static inline void tud_cdc_set_serial_state(cdc_serial_state_t ser_state) {
return tud_cdc_n_set_serial_state(0, ser_state);
}

TU_ATTR_ALWAYS_INLINE static inline void tud_cdc_get_line_coding(cdc_line_coding_t* coding) {
tud_cdc_n_get_line_coding(0, coding);
}
Expand Down
21 changes: 21 additions & 0 deletions src/common/tusb_types.h
Original file line number Diff line number Diff line change
Expand Up @@ -491,6 +491,27 @@ typedef struct TU_ATTR_PACKED {

TU_VERIFY_STATIC( sizeof(tusb_control_request_t) == 8, "size is not correct");


typedef struct TU_ATTR_PACKED {
union {
struct TU_ATTR_PACKED {
uint8_t recipient : 5; ///< Recipient type tusb_request_recipient_t.
uint8_t type : 2; ///< Request type tusb_request_type_t.
uint8_t direction : 1; ///< Direction type. tusb_dir_t
} bmRequestType_bit;

uint8_t bmRequestType;
};

uint8_t bNotification;
uint16_t wValue;
uint16_t wIndex;
uint16_t wLength;
} tusb_notification_t;

TU_VERIFY_STATIC( sizeof(tusb_notification_t) == 8, "size is not correct");


TU_ATTR_PACKED_END // End of all packed definitions
TU_ATTR_BIT_FIELD_ORDER_END

Expand Down
Loading