cdc_uart: add break handling

Implement break set/unset and declare the interface as capable of sending
line breaks.
diff --git a/src/cdc_uart.c b/src/cdc_uart.c
index 690d1cf..6379aad 100644
--- a/src/cdc_uart.c
+++ b/src/cdc_uart.c
@@ -32,7 +32,8 @@
 #include "probe_config.h"
 
 TaskHandle_t uart_taskhandle;
-TickType_t last_wake, interval = 100;
+TickType_t last_wake, interval = 100, break_expiry;
+bool timed_break;
 
 /* Max 1 FIFO worth of data */
 static uint8_t tx_buf[32];
@@ -68,11 +69,12 @@
 #endif
 }
 
-void cdc_task(void)
+bool cdc_task(void)
 {
     static int was_connected = 0;
     static uint cdc_tx_oe = 0;
     uint rx_len = 0;
+    bool keep_alive = false;
 
     // Consume uart fifo regardless even if not connected
     while(uart_is_readable(PROBE_UART_INTERFACE) && (rx_len < sizeof(rx_buf))) {
@@ -126,23 +128,38 @@
             gpio_put(PROBE_UART_TX_LED, 0);
 #endif
       }
+      /* Pending break handling */
+      if (timed_break) {
+        if (((int)break_expiry - (int)xTaskGetTickCount()) < 0) {
+          timed_break = false;
+          uart_set_break(PROBE_UART_INTERFACE, false);
+        } else {
+          keep_alive = true;
+        }
+      }
     } else if (was_connected) {
       tud_cdc_write_clear();
+      uart_set_break(PROBE_UART_INTERFACE, false);
+      timed_break = false;
       was_connected = 0;
       cdc_tx_oe = 0;
     }
+    return keep_alive;
 }
 
 void cdc_thread(void *ptr)
 {
   BaseType_t delayed;
   last_wake = xTaskGetTickCount();
+  bool keep_alive;
   /* Threaded with a polling interval that scales according to linerate */
   while (1) {
-    cdc_task();
-    delayed = xTaskDelayUntil(&last_wake, interval);
-    if (delayed == pdFALSE)
-      last_wake = xTaskGetTickCount();
+    keep_alive = cdc_task();
+    if (!keep_alive) {
+      delayed = xTaskDelayUntil(&last_wake, interval);
+        if (delayed == pdFALSE)
+          last_wake = xTaskGetTickCount();
+    }
   }
 }
 
@@ -236,3 +253,21 @@
   } else
     vTaskResume(uart_taskhandle);
 }
+
+void tud_cdc_send_break_cb(uint8_t itf, uint16_t wValue) {
+  switch(wValue) {
+    case 0:
+    uart_set_break(PROBE_UART_INTERFACE, false);
+    timed_break = false;
+    break;
+    case 0xffff:
+    uart_set_break(PROBE_UART_INTERFACE, true);
+    timed_break = false;
+    break;
+    default:
+    uart_set_break(PROBE_UART_INTERFACE, true);
+    timed_break = true;
+    break_expiry = xTaskGetTickCount() + (wValue * (configTICK_RATE_HZ / 1000));
+    break;
+  }
+}
diff --git a/src/cdc_uart.h b/src/cdc_uart.h
index 8782b64..db365c6 100644
--- a/src/cdc_uart.h
+++ b/src/cdc_uart.h
@@ -28,7 +28,7 @@
 
 void cdc_thread(void *ptr);
 void cdc_uart_init(void);
-void cdc_task(void);
+bool cdc_task(void);
 
 extern TaskHandle_t uart_taskhandle;
 
diff --git a/src/usb_descriptors.c b/src/usb_descriptors.c
index 9c49842..9558e7e 100644
--- a/src/usb_descriptors.c
+++ b/src/usb_descriptors.c
@@ -97,7 +97,7 @@
   return desc_hid_report;
 }
 
-uint8_t const desc_configuration[] =
+uint8_t desc_configuration[] =
 {
   TUD_CONFIG_DESCRIPTOR(1, ITF_NUM_TOTAL, 0, CONFIG_TOTAL_LEN, 0, 100),
   // Interface 0
@@ -121,6 +121,8 @@
 uint8_t const * tud_descriptor_configuration_cb(uint8_t index)
 {
   (void) index; // for multiple configurations
+  /* Hack in CAP_BREAK support */
+  desc_configuration[CONFIG_TOTAL_LEN - TUD_CDC_DESC_LEN + 8 + 9 + 5 + 5 + 4 - 1] = 0x6;
   return desc_configuration;
 }