/ examples / host_hid_to_device_cdc / host_hid_to_device_cdc.c
host_hid_to_device_cdc.c
  1  /*
  2   * The MIT License (MIT)
  3   *
  4   * Copyright (c) 2019 Ha Thach (tinyusb.org)
  5   *                    sekigon-gonnoc
  6   *
  7   * Permission is hereby granted, free of charge, to any person obtaining a copy
  8   * of this software and associated documentation files (the "Software"), to deal
  9   * in the Software without restriction, including without limitation the rights
 10   * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 11   * copies of the Software, and to permit persons to whom the Software is
 12   * furnished to do so, subject to the following conditions:
 13   *
 14   * The above copyright notice and this permission notice shall be included in
 15   * all copies or substantial portions of the Software.
 16   *
 17   * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 18   * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 19   * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 20   * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 21   * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 22   * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 23   * THE SOFTWARE.
 24   *
 25   */
 26  
 27  // This example runs both host and device concurrently. The USB host receive
 28  // reports from HID device and print it out over USB Device CDC interface.
 29  // For TinyUSB roothub port0 is native usb controller, roothub port1 is
 30  // pico-pio-usb.
 31  
 32  #include <stdlib.h>
 33  #include <stdio.h>
 34  #include <string.h>
 35  
 36  #include "hardware/clocks.h"
 37  #include "pico/stdlib.h"
 38  #include "pico/multicore.h"
 39  #include "pico/bootrom.h"
 40  
 41  #include "pio_usb.h"
 42  #include "tusb.h"
 43  
 44  //--------------------------------------------------------------------+
 45  // MACRO CONSTANT TYPEDEF PROTYPES
 46  //--------------------------------------------------------------------+
 47  
 48  // uncomment if you are using colemak layout
 49  // #define KEYBOARD_COLEMAK
 50  
 51  #ifdef KEYBOARD_COLEMAK
 52  const uint8_t colemak[128] = {
 53    0  ,  0,  0,  0,  0,  0,  0, 22,
 54    9  , 23,  7,  0, 24, 17,  8, 12,
 55    0  , 14, 28, 51,  0, 19, 21, 10,
 56    15 ,  0,  0,  0, 13,  0,  0,  0,
 57    0  ,  0,  0,  0,  0,  0,  0,  0,
 58    0  ,  0,  0,  0,  0,  0,  0,  0,
 59    0  ,  0,  0, 18,  0,  0,  0,  0,
 60    0  ,  0,  0,  0,  0,  0,  0,  0,
 61    0  ,  0,  0,  0,  0,  0,  0,  0,
 62    0  ,  0,  0,  0,  0,  0,  0,  0
 63  };
 64  #endif
 65  
 66  static uint8_t const keycode2ascii[128][2] =  { HID_KEYCODE_TO_ASCII };
 67  
 68  /*------------- MAIN -------------*/
 69  
 70  // core1: handle host events
 71  void core1_main() {
 72    sleep_ms(10);
 73  
 74    // Use tuh_configure() to pass pio configuration to the host stack
 75    // Note: tuh_configure() must be called before
 76    pio_usb_configuration_t pio_cfg = PIO_USB_DEFAULT_CONFIG;
 77    tuh_configure(1, TUH_CFGID_RPI_PIO_USB_CONFIGURATION, &pio_cfg);
 78  
 79    // To run USB SOF interrupt in core1, init host stack for pio_usb (roothub
 80    // port1) on core1
 81    tuh_init(1);
 82  
 83    while (true) {
 84      tuh_task(); // tinyusb host task
 85    }
 86  }
 87  
 88  // core0: handle device events
 89  int main(void) {
 90    // default 125MHz is not appropreate. Sysclock should be multiple of 12MHz.
 91    set_sys_clock_khz(120000, true);
 92  
 93    sleep_ms(10);
 94  
 95    multicore_reset_core1();
 96    // all USB task run in core1
 97    multicore_launch_core1(core1_main);
 98  
 99    // init device stack on native usb (roothub port0)
100    tud_init(0);
101  
102    while (true) {
103      tud_task(); // tinyusb device task
104      tud_cdc_write_flush();
105    }
106  
107    return 0;
108  }
109  
110  //--------------------------------------------------------------------+
111  // Device CDC
112  //--------------------------------------------------------------------+
113  
114  // Invoked when CDC interface received data from host
115  void tud_cdc_rx_cb(uint8_t itf)
116  {
117    (void) itf;
118  
119    char buf[64];
120    uint32_t count = tud_cdc_read(buf, sizeof(buf));
121  
122    // TODO control LED on keyboard of host stack
123    (void) count;
124  }
125  
126  //--------------------------------------------------------------------+
127  // Host HID
128  //--------------------------------------------------------------------+
129  
130  // Invoked when device with hid interface is mounted
131  // Report descriptor is also available for use. tuh_hid_parse_report_descriptor()
132  // can be used to parse common/simple enough descriptor.
133  // Note: if report descriptor length > CFG_TUH_ENUMERATION_BUFSIZE, it will be skipped
134  // therefore report_desc = NULL, desc_len = 0
135  void tuh_hid_mount_cb(uint8_t dev_addr, uint8_t instance, uint8_t const* desc_report, uint16_t desc_len)
136  {
137    (void)desc_report;
138    (void)desc_len;
139  
140    // Interface protocol (hid_interface_protocol_enum_t)
141    const char* protocol_str[] = { "None", "Keyboard", "Mouse" };
142    uint8_t const itf_protocol = tuh_hid_interface_protocol(dev_addr, instance);
143  
144    uint16_t vid, pid;
145    tuh_vid_pid_get(dev_addr, &vid, &pid);
146  
147    char tempbuf[256];
148    int count = sprintf(tempbuf, "[%04x:%04x][%u] HID Interface%u, Protocol = %s\r\n", vid, pid, dev_addr, instance, protocol_str[itf_protocol]);
149  
150    tud_cdc_write(tempbuf, count);
151    tud_cdc_write_flush();
152  
153    // Receive report from boot keyboard & mouse only
154    // tuh_hid_report_received_cb() will be invoked when report is available
155    if (itf_protocol == HID_ITF_PROTOCOL_KEYBOARD || itf_protocol == HID_ITF_PROTOCOL_MOUSE)
156    {
157      if ( !tuh_hid_receive_report(dev_addr, instance) )
158      {
159        tud_cdc_write_str("Error: cannot request report\r\n");
160      }
161    }
162  }
163  
164  // Invoked when device with hid interface is un-mounted
165  void tuh_hid_umount_cb(uint8_t dev_addr, uint8_t instance)
166  {
167    char tempbuf[256];
168    int count = sprintf(tempbuf, "[%u] HID Interface%u is unmounted\r\n", dev_addr, instance);
169    tud_cdc_write(tempbuf, count);
170    tud_cdc_write_flush();
171  }
172  
173  // look up new key in previous keys
174  static inline bool find_key_in_report(hid_keyboard_report_t const *report, uint8_t keycode)
175  {
176    for(uint8_t i=0; i<6; i++)
177    {
178      if (report->keycode[i] == keycode)  return true;
179    }
180  
181    return false;
182  }
183  
184  
185  // convert hid keycode to ascii and print via usb device CDC (ignore non-printable)
186  static void process_kbd_report(uint8_t dev_addr, hid_keyboard_report_t const *report)
187  {
188    (void) dev_addr;
189    static hid_keyboard_report_t prev_report = { 0, 0, {0} }; // previous report to check key released
190    bool flush = false;
191  
192    for(uint8_t i=0; i<6; i++)
193    {
194      uint8_t keycode = report->keycode[i];
195      if ( keycode )
196      {
197        if ( find_key_in_report(&prev_report, keycode) )
198        {
199          // exist in previous report means the current key is holding
200        }else
201        {
202          // not existed in previous report means the current key is pressed
203  
204          // remap the key code for Colemak layout
205          #ifdef KEYBOARD_COLEMAK
206          uint8_t colemak_key_code = colemak[keycode];
207          if (colemak_key_code != 0) keycode = colemak_key_code;
208          #endif
209  
210          bool const is_shift = report->modifier & (KEYBOARD_MODIFIER_LEFTSHIFT | KEYBOARD_MODIFIER_RIGHTSHIFT);
211          uint8_t ch = keycode2ascii[keycode][is_shift ? 1 : 0];
212  
213          if (ch)
214          {
215            if (ch == '\n') tud_cdc_write("\r", 1);
216            tud_cdc_write(&ch, 1);
217            flush = true;
218          }
219        }
220      }
221      // TODO example skips key released
222    }
223  
224    if (flush) tud_cdc_write_flush();
225  
226    prev_report = *report;
227  }
228  
229  // send mouse report to usb device CDC
230  static void process_mouse_report(uint8_t dev_addr, hid_mouse_report_t const * report)
231  {
232    //------------- button state  -------------//
233    //uint8_t button_changed_mask = report->buttons ^ prev_report.buttons;
234    char l = report->buttons & MOUSE_BUTTON_LEFT   ? 'L' : '-';
235    char m = report->buttons & MOUSE_BUTTON_MIDDLE ? 'M' : '-';
236    char r = report->buttons & MOUSE_BUTTON_RIGHT  ? 'R' : '-';
237  
238    char tempbuf[32];
239    int count = sprintf(tempbuf, "[%u] %c%c%c %d %d %d\r\n", dev_addr, l, m, r, report->x, report->y, report->wheel);
240  
241    tud_cdc_write(tempbuf, count);
242    tud_cdc_write_flush();
243  }
244  
245  // Invoked when received report from device via interrupt endpoint
246  void tuh_hid_report_received_cb(uint8_t dev_addr, uint8_t instance, uint8_t const* report, uint16_t len)
247  {
248    (void) len;
249    uint8_t const itf_protocol = tuh_hid_interface_protocol(dev_addr, instance);
250  
251    switch(itf_protocol)
252    {
253      case HID_ITF_PROTOCOL_KEYBOARD:
254        process_kbd_report(dev_addr, (hid_keyboard_report_t const*) report );
255      break;
256  
257      case HID_ITF_PROTOCOL_MOUSE:
258        process_mouse_report(dev_addr, (hid_mouse_report_t const*) report );
259      break;
260  
261      default: break;
262    }
263  
264    // continue to request to receive report
265    if ( !tuh_hid_receive_report(dev_addr, instance) )
266    {
267      tud_cdc_write_str("Error: cannot request report\r\n");
268    }
269  }