1+ /*
2+ * Copyright (c) 2020 The ZMK Contributors
3+ *
4+ * SPDX-License-Identifier: MIT
5+ */
6+
7+ #include <zephyr/device.h>
8+ #include <zephyr/init.h>
9+ #include <zephyr/kernel.h>
10+ #include <zephyr/logging/log.h>
11+
12+ #include "usbd_core.h"
13+ #include "usbd_hid.h"
14+
15+ #include <zmk/usb.h>
16+ #include <zmk/hid.h>
17+ #include <zmk/event_manager.h>
18+ #include <zmk/events/usb_conn_state_changed.h>
19+
20+ LOG_MODULE_DECLARE (zmk , CONFIG_ZMK_LOG_LEVEL );
21+
22+ #define CHERRYUSB_BUS_ID 0
23+
24+ /* USB status tracking, mirroring the CherryUSB event states */
25+ enum cherryusb_status {
26+ CUSB_STATUS_UNKNOWN ,
27+ CUSB_STATUS_CONNECTED ,
28+ CUSB_STATUS_DISCONNECTED ,
29+ CUSB_STATUS_CONFIGURED ,
30+ CUSB_STATUS_SUSPEND ,
31+ CUSB_STATUS_RESUME ,
32+ CUSB_STATUS_RESET ,
33+ CUSB_STATUS_ERROR ,
34+ };
35+
36+ static enum cherryusb_status usb_status = CUSB_STATUS_UNKNOWN ;
37+
38+ static void raise_usb_status_changed_event (struct k_work * _work ) {
39+ raise_zmk_usb_conn_state_changed (
40+ (struct zmk_usb_conn_state_changed ){.conn_state = zmk_usb_get_conn_state ()});
41+ }
42+
43+ K_WORK_DEFINE (usb_status_notifier_work , raise_usb_status_changed_event );
44+
45+ enum zmk_usb_conn_state zmk_usb_get_conn_state (void ) {
46+ LOG_DBG ("state: %d" , usb_status );
47+ switch (usb_status ) {
48+ case CUSB_STATUS_CONFIGURED :
49+ case CUSB_STATUS_SUSPEND :
50+ case CUSB_STATUS_RESUME :
51+ return ZMK_USB_CONN_HID ;
52+
53+ case CUSB_STATUS_DISCONNECTED :
54+ case CUSB_STATUS_UNKNOWN :
55+ return ZMK_USB_CONN_NONE ;
56+
57+ default :
58+ return ZMK_USB_CONN_POWERED ;
59+ }
60+ }
61+
62+ bool zmk_usb_is_hid_ready (void ) {
63+ return usb_status == CUSB_STATUS_CONFIGURED || usb_status == CUSB_STATUS_RESUME ;
64+ }
65+
66+ /* ---- USB Descriptors ---- */
67+
68+ #define USBD_VID CONFIG_USB_DEVICE_VID
69+ #define USBD_PID CONFIG_USB_DEVICE_PID
70+ #define USBD_MAX_POWER 100
71+ #define USBD_LANGID_STRING 1033
72+
73+ #define HID_INT_EP 0x81
74+ #define HID_INT_EP_SIZE 64
75+ #define HID_INT_EP_INTERVAL 1
76+
77+ #define USB_HID_CONFIG_DESC_SIZ (9 + HID_KEYBOARD_DESCRIPTOR_LEN)
78+
79+ static const uint8_t device_descriptor [] = {
80+ USB_DEVICE_DESCRIPTOR_INIT (USB_2_0 , 0x00 , 0x00 , 0x00 ,
81+ USBD_VID , USBD_PID , 0x0001 , 0x01 ),
82+ };
83+
84+ static const uint8_t config_descriptor [] = {
85+ USB_CONFIG_DESCRIPTOR_INIT (USB_HID_CONFIG_DESC_SIZ , 0x01 , 0x01 ,
86+ USB_CONFIG_BUS_POWERED | USB_CONFIG_REMOTE_WAKEUP ,
87+ USBD_MAX_POWER ),
88+ HID_KEYBOARD_DESCRIPTOR_INIT (0x00 ,
89+ #if IS_ENABLED (CONFIG_ZMK_USB_BOOT )
90+ 0x01 , /* boot interface subclass */
91+ #else
92+ 0x00 , /* no boot */
93+ #endif
94+ sizeof (zmk_hid_report_desc ),
95+ HID_INT_EP , HID_INT_EP_SIZE , HID_INT_EP_INTERVAL ),
96+ };
97+
98+ static const uint8_t device_quality_descriptor [] = {
99+ 0x0a ,
100+ USB_DESCRIPTOR_TYPE_DEVICE_QUALIFIER ,
101+ 0x00 , 0x02 ,
102+ 0x00 , 0x00 , 0x00 ,
103+ 0x40 ,
104+ 0x00 , 0x00 ,
105+ };
106+
107+ #ifndef CONFIG_USB_DEVICE_MANUFACTURER
108+ #define CONFIG_USB_DEVICE_MANUFACTURER "ZMK Project"
109+ #endif
110+
111+ static const char * string_descriptors [] = {
112+ (const char []){ 0x09 , 0x04 }, /* Langid */
113+ CONFIG_USB_DEVICE_MANUFACTURER , /* Manufacturer */
114+ CONFIG_ZMK_KEYBOARD_NAME , /* Product */
115+ "ZMK000000" , /* Serial Number */
116+ };
117+
118+ static const uint8_t * device_descriptor_callback (uint8_t speed ) {
119+ (void )speed ;
120+ return device_descriptor ;
121+ }
122+
123+ static const uint8_t * config_descriptor_callback (uint8_t speed ) {
124+ (void )speed ;
125+ return config_descriptor ;
126+ }
127+
128+ static const uint8_t * device_quality_descriptor_callback (uint8_t speed ) {
129+ (void )speed ;
130+ return device_quality_descriptor ;
131+ }
132+
133+ static const char * string_descriptor_callback (uint8_t speed , uint8_t index ) {
134+ (void )speed ;
135+ if (index >= ARRAY_SIZE (string_descriptors )) {
136+ return NULL ;
137+ }
138+ return string_descriptors [index ];
139+ }
140+
141+ const struct usb_descriptor zmk_cherryusb_descriptor = {
142+ .device_descriptor_callback = device_descriptor_callback ,
143+ .config_descriptor_callback = config_descriptor_callback ,
144+ .device_quality_descriptor_callback = device_quality_descriptor_callback ,
145+ .string_descriptor_callback = string_descriptor_callback ,
146+ };
147+
148+ /* ---- CherryUSB event handler ---- */
149+
150+ static void usbd_event_handler (uint8_t busid , uint8_t event ) {
151+ LOG_DBG ("USB event: bus %d, event %d\n" , busid , event );
152+ switch (event ) {
153+ case USBD_EVENT_RESET :
154+ #if IS_ENABLED (CONFIG_ZMK_USB_BOOT )
155+ extern void zmk_usb_hid_set_protocol (uint8_t protocol );
156+ zmk_usb_hid_set_protocol (1 ); /* HID_PROTOCOL_REPORT */
157+ #endif
158+ usb_status = CUSB_STATUS_RESET ;
159+ break ;
160+ case USBD_EVENT_CONNECTED :
161+ usb_status = CUSB_STATUS_CONNECTED ;
162+ break ;
163+ case USBD_EVENT_DISCONNECTED :
164+ usb_status = CUSB_STATUS_DISCONNECTED ;
165+ break ;
166+ case USBD_EVENT_CONFIGURED :
167+ usb_status = CUSB_STATUS_CONFIGURED ;
168+ break ;
169+ case USBD_EVENT_SUSPEND :
170+ usb_status = CUSB_STATUS_SUSPEND ;
171+ break ;
172+ case USBD_EVENT_RESUME :
173+ usb_status = CUSB_STATUS_RESUME ;
174+ break ;
175+ case USBD_EVENT_ERROR :
176+ usb_status = CUSB_STATUS_ERROR ;
177+ break ;
178+ default :
179+ return ;
180+ }
181+ k_work_submit (& usb_status_notifier_work );
182+ }
183+
184+ /* ---- HID interface and endpoint (shared with usb_hid.c) ---- */
185+
186+ struct usbd_interface zmk_cherryusb_hid_intf ;
187+
188+ static void hid_in_ep_callback (uint8_t busid , uint8_t ep , uint32_t nbytes );
189+
190+ struct usbd_endpoint zmk_cherryusb_hid_in_ep = {
191+ .ep_cb = hid_in_ep_callback ,
192+ .ep_addr = HID_INT_EP ,
193+ };
194+
195+ /* Semaphore for EP write completion, given from endpoint callback */
196+ K_SEM_DEFINE (zmk_cherryusb_hid_sem , 1 , 1 );
197+
198+ static void hid_in_ep_callback (uint8_t busid , uint8_t ep , uint32_t nbytes ) {
199+ (void )busid ;
200+ (void )ep ;
201+ (void )nbytes ;
202+ k_sem_give (& zmk_cherryusb_hid_sem );
203+ }
204+
205+ static int zmk_usb_init (void ) {
206+ usbd_desc_register (CHERRYUSB_BUS_ID , & zmk_cherryusb_descriptor );
207+
208+ usbd_add_interface (CHERRYUSB_BUS_ID ,
209+ usbd_hid_init_intf (CHERRYUSB_BUS_ID , & zmk_cherryusb_hid_intf ,
210+ zmk_hid_report_desc ,
211+ sizeof (zmk_hid_report_desc )));
212+ usbd_add_endpoint (CHERRYUSB_BUS_ID , & zmk_cherryusb_hid_in_ep );
213+
214+ int ret = usbd_initialize (CHERRYUSB_BUS_ID ,
215+ (uintptr_t )CONFIG_CHERRYUSB_DEV_BASE_ADDR ,
216+ usbd_event_handler );
217+ if (ret != 0 ) {
218+ LOG_ERR ("CherryUSB initialization failed (err %d)" , ret );
219+ return - EINVAL ;
220+ }
221+
222+ LOG_INF ("CherryUSB initialized" );
223+ return 0 ;
224+ }
225+
226+ SYS_INIT (zmk_usb_init , APPLICATION , CONFIG_ZMK_USB_INIT_PRIORITY );
0 commit comments