1 /*
2  * SPDX-FileCopyrightText: 2021-2022 Espressif Systems (Shanghai) CO LTD
3  *
4  * SPDX-License-Identifier: Unlicense OR CC0-1.0
5  */
6 
7 #include <stdlib.h>
8 #include "freertos/FreeRTOS.h"
9 #include "freertos/semphr.h"
10 #include "esp_log.h"
11 #include "usb/usb_host.h"
12 
13 #define CLIENT_NUM_EVENT_MSG        5
14 
15 #define ACTION_OPEN_DEV             0x01
16 #define ACTION_GET_DEV_INFO         0x02
17 #define ACTION_GET_DEV_DESC         0x04
18 #define ACTION_GET_CONFIG_DESC      0x08
19 #define ACTION_GET_STR_DESC         0x10
20 #define ACTION_CLOSE_DEV            0x20
21 #define ACTION_EXIT                 0x40
22 
23 typedef struct {
24     usb_host_client_handle_t client_hdl;
25     uint8_t dev_addr;
26     usb_device_handle_t dev_hdl;
27     uint32_t actions;
28 } class_driver_t;
29 
30 static const char *TAG = "CLASS";
31 
client_event_cb(const usb_host_client_event_msg_t * event_msg,void * arg)32 static void client_event_cb(const usb_host_client_event_msg_t *event_msg, void *arg)
33 {
34     class_driver_t *driver_obj = (class_driver_t *)arg;
35     switch (event_msg->event) {
36         case USB_HOST_CLIENT_EVENT_NEW_DEV:
37             if (driver_obj->dev_addr == 0) {
38                 driver_obj->dev_addr = event_msg->new_dev.address;
39                 //Open the device next
40                 driver_obj->actions |= ACTION_OPEN_DEV;
41             }
42             break;
43         case USB_HOST_CLIENT_EVENT_DEV_GONE:
44             if (driver_obj->dev_hdl != NULL) {
45                 //Cancel any other actions and close the device next
46                 driver_obj->actions = ACTION_CLOSE_DEV;
47             }
48             break;
49         default:
50             //Should never occur
51             abort();
52     }
53 }
54 
action_open_dev(class_driver_t * driver_obj)55 static void action_open_dev(class_driver_t *driver_obj)
56 {
57     assert(driver_obj->dev_addr != 0);
58     ESP_LOGI(TAG, "Opening device at address %d", driver_obj->dev_addr);
59     ESP_ERROR_CHECK(usb_host_device_open(driver_obj->client_hdl, driver_obj->dev_addr, &driver_obj->dev_hdl));
60     //Get the device's information next
61     driver_obj->actions &= ~ACTION_OPEN_DEV;
62     driver_obj->actions |= ACTION_GET_DEV_INFO;
63 }
64 
action_get_info(class_driver_t * driver_obj)65 static void action_get_info(class_driver_t *driver_obj)
66 {
67     assert(driver_obj->dev_hdl != NULL);
68     ESP_LOGI(TAG, "Getting device information");
69     usb_device_info_t dev_info;
70     ESP_ERROR_CHECK(usb_host_device_info(driver_obj->dev_hdl, &dev_info));
71     ESP_LOGI(TAG, "\t%s speed", (dev_info.speed == USB_SPEED_LOW) ? "Low" : "Full");
72     ESP_LOGI(TAG, "\tbConfigurationValue %d", dev_info.bConfigurationValue);
73     //Todo: Print string descriptors
74 
75     //Get the device descriptor next
76     driver_obj->actions &= ~ACTION_GET_DEV_INFO;
77     driver_obj->actions |= ACTION_GET_DEV_DESC;
78 }
79 
action_get_dev_desc(class_driver_t * driver_obj)80 static void action_get_dev_desc(class_driver_t *driver_obj)
81 {
82     assert(driver_obj->dev_hdl != NULL);
83     ESP_LOGI(TAG, "Getting device descriptor");
84     const usb_device_desc_t *dev_desc;
85     ESP_ERROR_CHECK(usb_host_get_device_descriptor(driver_obj->dev_hdl, &dev_desc));
86     usb_print_device_descriptor(dev_desc);
87     //Get the device's config descriptor next
88     driver_obj->actions &= ~ACTION_GET_DEV_DESC;
89     driver_obj->actions |= ACTION_GET_CONFIG_DESC;
90 }
91 
action_get_config_desc(class_driver_t * driver_obj)92 static void action_get_config_desc(class_driver_t *driver_obj)
93 {
94     assert(driver_obj->dev_hdl != NULL);
95     ESP_LOGI(TAG, "Getting config descriptor");
96     const usb_config_desc_t *config_desc;
97     ESP_ERROR_CHECK(usb_host_get_active_config_descriptor(driver_obj->dev_hdl, &config_desc));
98     usb_print_config_descriptor(config_desc, NULL);
99     //Get the device's string descriptors next
100     driver_obj->actions &= ~ACTION_GET_CONFIG_DESC;
101     driver_obj->actions |= ACTION_GET_STR_DESC;
102 }
103 
action_get_str_desc(class_driver_t * driver_obj)104 static void action_get_str_desc(class_driver_t *driver_obj)
105 {
106     assert(driver_obj->dev_hdl != NULL);
107     usb_device_info_t dev_info;
108     ESP_ERROR_CHECK(usb_host_device_info(driver_obj->dev_hdl, &dev_info));
109     if (dev_info.str_desc_manufacturer) {
110         ESP_LOGI(TAG, "Getting Manufacturer string descriptor");
111         usb_print_string_descriptor(dev_info.str_desc_manufacturer);
112     }
113     if (dev_info.str_desc_product) {
114         ESP_LOGI(TAG, "Getting Product string descriptor");
115         usb_print_string_descriptor(dev_info.str_desc_product);
116     }
117     if (dev_info.str_desc_serial_num) {
118         ESP_LOGI(TAG, "Getting Serial Number string descriptor");
119         usb_print_string_descriptor(dev_info.str_desc_serial_num);
120     }
121     //Nothing to do until the device disconnects
122     driver_obj->actions &= ~ACTION_GET_STR_DESC;
123 }
124 
aciton_close_dev(class_driver_t * driver_obj)125 static void aciton_close_dev(class_driver_t *driver_obj)
126 {
127     ESP_ERROR_CHECK(usb_host_device_close(driver_obj->client_hdl, driver_obj->dev_hdl));
128     driver_obj->dev_hdl = NULL;
129     driver_obj->dev_addr = 0;
130     //We need to exit the event handler loop
131     driver_obj->actions &= ~ACTION_CLOSE_DEV;
132     driver_obj->actions |= ACTION_EXIT;
133 }
134 
class_driver_task(void * arg)135 void class_driver_task(void *arg)
136 {
137     SemaphoreHandle_t signaling_sem = (SemaphoreHandle_t)arg;
138     class_driver_t driver_obj = {0};
139 
140     //Wait until daemon task has installed USB Host Library
141     xSemaphoreTake(signaling_sem, portMAX_DELAY);
142 
143     ESP_LOGI(TAG, "Registering Client");
144     usb_host_client_config_t client_config = {
145         .is_synchronous = false,    //Synchronous clients currently not supported. Set this to false
146         .max_num_event_msg = CLIENT_NUM_EVENT_MSG,
147         .async = {
148             .client_event_callback = client_event_cb,
149             .callback_arg = (void *)&driver_obj,
150         },
151     };
152     ESP_ERROR_CHECK(usb_host_client_register(&client_config, &driver_obj.client_hdl));
153 
154     while (1) {
155         if (driver_obj.actions == 0) {
156             usb_host_client_handle_events(driver_obj.client_hdl, portMAX_DELAY);
157         } else {
158             if (driver_obj.actions & ACTION_OPEN_DEV) {
159                 action_open_dev(&driver_obj);
160             }
161             if (driver_obj.actions & ACTION_GET_DEV_INFO) {
162                 action_get_info(&driver_obj);
163             }
164             if (driver_obj.actions & ACTION_GET_DEV_DESC) {
165                 action_get_dev_desc(&driver_obj);
166             }
167             if (driver_obj.actions & ACTION_GET_CONFIG_DESC) {
168                 action_get_config_desc(&driver_obj);
169             }
170             if (driver_obj.actions & ACTION_GET_STR_DESC) {
171                 action_get_str_desc(&driver_obj);
172             }
173             if (driver_obj.actions & ACTION_CLOSE_DEV) {
174                 aciton_close_dev(&driver_obj);
175             }
176             if (driver_obj.actions & ACTION_EXIT) {
177                 break;
178             }
179         }
180     }
181 
182     ESP_LOGI(TAG, "Deregistering Client");
183     ESP_ERROR_CHECK(usb_host_client_deregister(driver_obj.client_hdl));
184 
185     //Wait to be deleted
186     xSemaphoreGive(signaling_sem);
187     vTaskSuspend(NULL);
188 }
189