bluepill-serial-monster icon indicating copy to clipboard operation
bluepill-serial-monster copied to clipboard

Feature request: Add support for SendBreak requests

Open cyrozap opened this issue 1 year ago • 0 comments

From the spec:

This request sends special carrier modulation that generates an RS-232 style break.

The wValue field contains the length of time, in milliseconds, of the break signal. If wValue contains a value of FFFFh, then the device will send a break until another SendBreak request is received with the wValue of 0000h.

The code that follows is my hacky attempt at implementing the functionality. I call it "hacky" because I used a loop to implement the delay time (which IIRC isn't accurate at all--I don't remember exactly because it's been almost 18 months since I last worked on this), and the pin mode change involves using memcpy to clone the old TX pin configuration, change it to GPIO, and then use the modified configuration to re-init the pin. Also, I'm not sure if it's a good idea to handle the delay inside the USB control endpoint event handler, since the delay can in theory be up to 65.534 seconds long. Also, now that I've thought about this more, if the delay was done outside of the event handler, what should be done if an indefinite SendBreak is sent, and then TX data is sent? Since the break is just holding the TX line low, if that data was transmitted during that time it would all be lost.

If anyone knows a better way to implement this, please let me know.

diff --git a/usb_cdc.c b/usb_cdc.c
index b48f4b5..300466c 100644
--- a/usb_cdc.c
+++ b/usb_cdc.c
@@ -241,6 +241,27 @@ static void usb_cdc_set_port_txa(int port, int txa_active) {
     }
 }
 
+static usb_status_t usb_cdc_send_break(int port, uint16_t duration_ms) {
+    if (port < USB_CDC_NUM_PORTS) {
+        const gpio_pin_t *tx_pin = &device_config_get()->cdc_config.port_config[port].pins[cdc_pin_tx];
+        if (duration_ms != 0) {
+            gpio_pin_t tx_pin_new = { 0 };
+            memcpy(&tx_pin_new, tx_pin, sizeof(gpio_pin_t));
+            tx_pin_new.func = gpio_func_general;
+            gpio_pin_init(&tx_pin_new);
+            gpio_pin_set(tx_pin, 0);
+        }
+        if (duration_ms != 0xffff) {
+            for (int i=0; i < 8000*duration_ms; i++) {
+                __NOP();
+            }
+            gpio_pin_set(tx_pin, 1);
+            gpio_pin_init(tx_pin);
+        }
+    }
+    return usb_status_ack;
+}
+
 static usb_status_t usb_cdc_set_control_line_state(int port, uint16_t state) {
     usb_cdc_set_port_dtr(port, (state & USB_CDC_CONTROL_LINE_STATE_DTR_MASK));
     usb_cdc_set_port_rts(port, (state & USB_CDC_CONTROL_LINE_STATE_RTS_MASK));
@@ -741,6 +762,8 @@ usb_status_t usb_cdc_ctrl_process_request(usb_setup_t *setup, void **payload,
                     return usb_status_ack;
                 }
                 break;
+            case usb_cdc_request_send_break:
+                return usb_cdc_send_break(port, setup->wValue);
             default:
                 ;
             }
diff --git a/usb_cdc.h b/usb_cdc.h
index c37bc7c..86b01eb 100644
--- a/usb_cdc.h
+++ b/usb_cdc.h
@@ -42,7 +42,7 @@ typedef enum {
 #define USB_CDC_ACM_CAPABILITY_SEND_BREAK           0x04
 #define USB_CDC_ACM_CAPABILITY_NETWORK_CONNECTION   0x08
 
-#define USB_CDC_ACM_CAPABILITIES (USB_CDC_ACM_CAPABILITY_LINE_CODING)
+#define USB_CDC_ACM_CAPABILITIES (USB_CDC_ACM_CAPABILITY_SEND_BREAK | USB_CDC_ACM_CAPABILITY_LINE_CODING)
 
 /* USB CDC Header Functional Descriptor */
 

cyrozap avatar Mar 13 '23 06:03 cyrozap