summaryrefslogtreecommitdiff
path: root/core/usbh_core.h
blob: abd193df9faf1546a5eaea08795eacb44a86cf48 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
/*
 * Copyright (c) 2022, sakumisu
 *
 * SPDX-License-Identifier: Apache-2.0
 */
#ifndef USBH_CORE_H
#define USBH_CORE_H

#include <stdbool.h>
#include <string.h>
#include <stdint.h>
#include <stdlib.h>

#include "usb_config.h"
#include "usb_util.h"
#include "usb_errno.h"
#include "usb_def.h"
#include "usb_list.h"
#include "usb_log.h"
#include "usb_hc.h"
#include "usb_osal.h"
#include "usbh_hub.h"
#include "usb_memcpy.h"
#include "usb_ringbuffer.h"
#include "usb_mempool.h"
#include "usb_dcache.h"
#include "usb_version.h"

#ifdef __cplusplus
extern "C" {
#endif

enum usbh_event_type {
    /* USB HCD IRQ */
    USBH_EVENT_ERROR,
    USBH_EVENT_SOF,

    /* USB DEVICE STATUS */
    USBH_EVENT_DEVICE_RESET,
    USBH_EVENT_DEVICE_CONNECTED,
    USBH_EVENT_DEVICE_DISCONNECTED,
    USBH_EVENT_DEVICE_CONFIGURED,
    USBH_EVENT_DEVICE_WAKEUP,
    USBH_EVENT_DEVICE_SUSPEND,
    USBH_EVENT_DEVICE_RESUME,

    /* USB DEVICE INTERFACE STATUS */
    USBH_EVENT_INTERFACE_UNSUPPORTED,
    USBH_EVENT_INTERFACE_START,
    USBH_EVENT_INTERFACE_STOP,

    /* USB FRAMEWORK STATUS */
    USBH_EVENT_INIT,
    USBH_EVENT_DEINIT,
    USBH_EVENT_UNKNOWN,
};

#define USB_HUB_PORT_ANY  0
#define USB_HUB_INDEX_ANY 0
#define USB_INTERFACE_ANY 0xff

#define USB_CLASS_MATCH_VENDOR        0x0001
#define USB_CLASS_MATCH_PRODUCT       0x0002
#define USB_CLASS_MATCH_INTF_CLASS    0x0004
#define USB_CLASS_MATCH_INTF_SUBCLASS 0x0008
#define USB_CLASS_MATCH_INTF_PROTOCOL 0x0010
#define USB_CLASS_MATCH_INTF_NUM      0x0020
#define USB_CLASS_MATCH_VID_PID       (USB_CLASS_MATCH_VENDOR | USB_CLASS_MATCH_PRODUCT)

#define CLASS_CONNECT(hport, i)    ((hport)->config.intf[i].class_driver->connect(hport, i))
#define CLASS_DISCONNECT(hport, i) ((hport)->config.intf[i].class_driver->disconnect(hport, i))

#ifdef __ARMCC_VERSION /* ARM C Compiler */
#define CLASS_INFO_DEFINE __attribute__((section("usbh_class_info"))) __USED __ALIGNED(1)
#elif defined(__GNUC__)
#define CLASS_INFO_DEFINE __attribute__((section(".usbh_class_info"))) __USED __ALIGNED(1)
#elif defined(__ICCARM__) || defined(__ICCRX__) || defined(__ICCRISCV__)
#pragma section = ".usbh_class_info"
#define CLASS_INFO_DEFINE __attribute__((section(".usbh_class_info"))) __USED __ALIGNED(1)
#endif

#define USBH_GET_URB_INTERVAL(interval, speed) (speed < USB_SPEED_HIGH ? (interval * 1000) : ((1 << (interval - 1)) * 125))

#define USBH_EP_INIT(ep, ep_desc)                                            \
    do {                                                                     \
        ep = ep_desc;                                                        \
        USB_LOG_INFO("Ep=%02x Attr=%02u Mps=%d Interval=%02u Mult=%02u\r\n", \
                     ep_desc->bEndpointAddress,                              \
                     ep_desc->bmAttributes,                                  \
                     USB_GET_MAXPACKETSIZE(ep_desc->wMaxPacketSize),         \
                     ep_desc->bInterval,                                     \
                     USB_GET_MULT(ep_desc->wMaxPacketSize));                 \
    } while (0)

typedef void (*usbh_event_handler_t)(uint8_t busid, uint8_t hub_index, uint8_t hub_port, uint8_t intf, uint8_t event);

struct usbh_class_info {
    uint8_t match_flags;           /* Used for product specific matches; range is inclusive */
    uint8_t bInterfaceClass;       /* Base device class code */
    uint8_t bInterfaceSubClass;    /* Sub-class, depends on base class. Eg. */
    uint8_t bInterfaceProtocol;    /* Protocol, depends on base class. Eg. */
    uint8_t bInterfaceNumber;      /* Interface number */
    const uint16_t (*id_table)[2]; /* List of Vendor/Product ID pairs */
    const struct usbh_class_driver *class_driver;
};

struct usbh_hubport;
struct usbh_class_driver {
    const char *driver_name;
    int (*connect)(struct usbh_hubport *hport, uint8_t intf);
    int (*disconnect)(struct usbh_hubport *hport, uint8_t intf);
};

struct usbh_endpoint {
    struct usb_endpoint_descriptor ep_desc;
};

struct usbh_interface_altsetting {
    struct usb_interface_descriptor intf_desc;
    struct usbh_endpoint ep[CONFIG_USBHOST_MAX_ENDPOINTS];
};

struct usbh_interface {
    char devname[CONFIG_USBHOST_DEV_NAMELEN];
    struct usbh_class_driver *class_driver;
    void *priv;
    struct usbh_interface_altsetting altsetting[CONFIG_USBHOST_MAX_INTF_ALTSETTINGS];
    uint8_t altsetting_num;
};

struct usbh_configuration {
    struct usb_configuration_descriptor config_desc;
    struct usbh_interface intf[CONFIG_USBHOST_MAX_INTERFACES];
};

struct usbh_hubport {
    bool connected;   /* True: device connected; false: disconnected */
    uint8_t port;     /* Hub port index */
    uint8_t dev_addr; /* device address */
    uint8_t speed;    /* device speed */
    uint8_t depth;    /* distance from root hub */
    uint8_t route;    /* route string */
    uint8_t slot_id;  /* slot id */
    struct usb_device_descriptor device_desc;
    struct usbh_configuration config;
    const char *iManufacturer;
    const char *iProduct;
    const char *iSerialNumber;
    uint8_t *raw_config_desc;
    struct usb_setup_packet *setup;
    struct usbh_hub *parent;
    struct usbh_hub *self; /* if this hubport is a hub */
    struct usbh_bus *bus;
    struct usb_endpoint_descriptor ep0;
    struct usbh_urb ep0_urb;
    usb_osal_mutex_t mutex;
};

struct usbh_hub {
    bool connected;
    bool is_roothub;
    uint8_t index;
    uint8_t hub_addr;
    uint8_t speed;
    uint8_t nports;
    uint8_t powerdelay;
    uint8_t tt_think;
    bool ismtt;
    struct usb_hub_descriptor hub_desc;       /* USB 2.0 only */
    struct usb_hub_ss_descriptor hub_ss_desc; /* USB 3.0 only */
    struct usbh_hubport child[CONFIG_USBHOST_MAX_EHPORTS];
    struct usbh_hubport *parent;
    struct usbh_bus *bus;
    struct usb_endpoint_descriptor *intin;
    struct usbh_urb intin_urb;
    uint8_t *int_buffer;
    struct usb_osal_timer *int_timer;
};

struct usbh_devaddr_map {
    /**
     * alloctab[0]:addr from 0~31
     * alloctab[1]:addr from 32~63
     * alloctab[2]:addr from 64~95
     * alloctab[3]:addr from 96~127
     *
     */
    uint8_t last;         /* Last device address */
    uint32_t alloctab[4]; /* Bit allocation table */
};

struct usbh_hcd {
    uintptr_t reg_base;
    uint8_t hcd_id;
    uint8_t roothub_intbuf[2]; /* at most 15 roothub ports */
    struct usbh_hub roothub;
};

struct usbh_bus {
    usb_slist_t list;
    uint8_t busid;
    struct usbh_hcd hcd;
    struct usbh_devaddr_map devgen;
    usb_osal_thread_t hub_thread;
    usb_osal_mq_t hub_mq;
    usb_osal_sem_t hub_sem;
    usbh_event_handler_t event_handler;
};

static inline void usbh_control_urb_fill(struct usbh_urb *urb,
                                         struct usbh_hubport *hport,
                                         struct usb_setup_packet *setup,
                                         uint8_t *transfer_buffer,
                                         uint32_t transfer_buffer_length,
                                         uint32_t timeout,
                                         usbh_complete_callback_t complete,
                                         void *arg)
{
    urb->hport = hport;
    urb->ep = &hport->ep0;
    urb->setup = setup;
    urb->transfer_buffer = transfer_buffer;
    urb->transfer_buffer_length = transfer_buffer_length;
    urb->timeout = timeout;
    urb->complete = complete;
    urb->arg = arg;
}

static inline void usbh_bulk_urb_fill(struct usbh_urb *urb,
                                      struct usbh_hubport *hport,
                                      struct usb_endpoint_descriptor *ep,
                                      uint8_t *transfer_buffer,
                                      uint32_t transfer_buffer_length,
                                      uint32_t timeout,
                                      usbh_complete_callback_t complete,
                                      void *arg)
{
    urb->hport = hport;
    urb->ep = ep;
    urb->setup = NULL;
    urb->transfer_buffer = transfer_buffer;
    urb->transfer_buffer_length = transfer_buffer_length;
    urb->timeout = timeout;
    urb->complete = complete;
    urb->arg = arg;
}

static inline void usbh_int_urb_fill(struct usbh_urb *urb,
                                     struct usbh_hubport *hport,
                                     struct usb_endpoint_descriptor *ep,
                                     uint8_t *transfer_buffer,
                                     uint32_t transfer_buffer_length,
                                     uint32_t timeout,
                                     usbh_complete_callback_t complete,
                                     void *arg)
{
    urb->hport = hport;
    urb->ep = ep;
    urb->setup = NULL;
    urb->transfer_buffer = transfer_buffer;
    urb->transfer_buffer_length = transfer_buffer_length;
    urb->timeout = timeout;
    urb->complete = complete;
    urb->arg = arg;
    urb->interval = USBH_GET_URB_INTERVAL(ep->bInterval, hport->speed);
}

extern struct usbh_bus g_usbhost_bus[];
#ifdef USBH_IRQHandler
#error USBH_IRQHandler is obsolete, please call USBH_IRQHandler(xxx) in your irq
#endif

/**
 * @brief Submit an control transfer to an endpoint.
 * This is a blocking method; this method will not return until the transfer has completed.
 * Default timeout is 500ms.
 *
 * @param pipe The control endpoint to send/receive the control request.
 * @param setup Setup packet to be sent.
 * @param buffer buffer used for sending the request and for returning any responses.
 * @return On success will return 0, and others indicate fail.
 */
int usbh_control_transfer(struct usbh_hubport *hport, struct usb_setup_packet *setup, uint8_t *buffer);

/**
 * @brief Retrieves a USB string descriptor from a specific hub port.
 *
 * This function is responsible for retrieving the USB string descriptor
 * with the specified index from the USB device connected to the given hub port.
 * The retrieved descriptor is stored in the output buffer provided.
 *
 * @param hport Pointer to the USB hub port structure.
 * @param index Index of the string descriptor to retrieve.
 * @param output Pointer to the buffer where the retrieved descriptor will be stored.
 * @param output_len Length of the output buffer.
 * @return On success will return 0, and others indicate fail.
 */
int usbh_get_string_desc(struct usbh_hubport *hport, uint8_t index, uint8_t *output, uint16_t output_len);

/**
 * @brief Sets the alternate setting for a USB interface on a specific hub port.
 *
 * This function is responsible for setting the alternate setting of the
 * specified USB interface on the USB device connected to the given hub port.
 * The interface and alternate setting are identified by the respective parameters.
 *
 * @param hport Pointer to the USB hub port structure.
 * @param intf Interface number to set the alternate setting for.
 * @param altsetting Alternate setting value to set for the interface.
 * @return On success will return 0, and others indicate fail.
 */
int usbh_set_interface(struct usbh_hubport *hport, uint8_t intf, uint8_t altsetting);

int usbh_initialize(uint8_t busid, uintptr_t reg_base, usbh_event_handler_t event_handler);
int usbh_deinitialize(uint8_t busid);
void *usbh_find_class_instance(const char *devname);
struct usbh_hubport *usbh_find_hubport(uint8_t busid, uint8_t hub_index, uint8_t hub_port);
uint8_t usbh_get_hport_active_config_index(struct usbh_hubport *hport);

int lsusb(int argc, char **argv);

#ifdef __cplusplus
}
#endif

#endif /* USBH_CORE_H */