aboutsummaryrefslogtreecommitdiffstats
path: root/os/hal/include/hal_usbh.h
blob: aa4920589eac1ea22736a58cb7ff115cd9328318 (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
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
/*
    ChibiOS - Copyright (C) 2006..2017 Giovanni Di Sirio
              Copyright (C) 2015..2019 Diego Ismirlian, (dismirlian(at)google's mail)

    Licensed under the Apache License, Version 2.0 (the "License");
    you may not use this file except in compliance with the License.
    You may obtain a copy of the License at

        http://www.apache.org/licenses/LICENSE-2.0

    Unless required by applicable law or agreed to in writing, software
    distributed under the License is distributed on an "AS IS" BASIS,
    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    See the License for the specific language governing permissions and
    limitations under the License.
*/

#ifndef HAL_USBH_H_
#define HAL_USBH_H_

#include "hal.h"

#ifndef HAL_USE_USBH
#define HAL_USE_USBH FALSE
#endif

#ifndef HAL_USBH_USE_FTDI
#define HAL_USBH_USE_FTDI FALSE
#endif

#ifndef HAL_USBH_USE_HUB
#define HAL_USBH_USE_HUB FALSE
#endif

#ifndef HAL_USBH_USE_MSD
#define HAL_USBH_USE_MSD FALSE
#endif

#ifndef HAL_USBH_USE_UVC
#define HAL_USBH_USE_UVC FALSE
#endif

#ifndef HAL_USBH_USE_AOA
#define HAL_USBH_USE_AOA FALSE
#endif

#ifndef HAL_USBH_USE_HID
#define HAL_USBH_USE_HID FALSE
#endif

#ifndef HAL_USBH_USE_ADDITIONAL_CLASS_DRIVERS
#define HAL_USBH_USE_ADDITIONAL_CLASS_DRIVERS	FALSE
#endif

#ifndef HAL_USBH_USE_IAD
#define HAL_USBH_USE_IAD     HAL_USBH_USE_UVC
#endif

#if (HAL_USE_USBH == TRUE) || defined(__DOXYGEN__)

#include "osal.h"
#include "usbh/list.h"
#include "usbh/defs.h"

/*===========================================================================*/
/* Derived constants and error checks.                                       */
/*===========================================================================*/

#if !HAL_USBH_USE_HUB
#define USBH_MAX_ADDRESSES				1
#else
#define USBH_MAX_ADDRESSES				(HAL_USBHHUB_MAX_PORTS + 1)
#endif

enum usbh_status {
	USBH_STATUS_STOPPED = 0,
	USBH_STATUS_STARTED,
	USBH_STATUS_SUSPENDED,
};

/* These correspond to the USB spec */
enum usbh_devstatus {
	USBH_DEVSTATUS_DISCONNECTED = 0,
	USBH_DEVSTATUS_ATTACHED,
	USBH_DEVSTATUS_CONNECTED,
	USBH_DEVSTATUS_DEFAULT,
	USBH_DEVSTATUS_ADDRESS,
	USBH_DEVSTATUS_CONFIGURED,
};

enum usbh_devspeed {
	USBH_DEVSPEED_LOW = 0,
	USBH_DEVSPEED_FULL,
	USBH_DEVSPEED_HIGH,
};

enum usbh_epdir {
	USBH_EPDIR_IN		= 0x80,
	USBH_EPDIR_OUT		= 0
};

enum usbh_eptype {
	USBH_EPTYPE_CTRL	= 0,
	USBH_EPTYPE_ISO		= 1,
	USBH_EPTYPE_BULK	= 2,
	USBH_EPTYPE_INT		= 3,
};

enum usbh_epstatus {
	USBH_EPSTATUS_UNINITIALIZED = 0,
	USBH_EPSTATUS_CLOSED,
	USBH_EPSTATUS_OPEN,
	USBH_EPSTATUS_HALTED,
};

enum usbh_urbstatus {
	USBH_URBSTATUS_UNINITIALIZED = 0,
	USBH_URBSTATUS_INITIALIZED,
	USBH_URBSTATUS_PENDING,
	USBH_URBSTATUS_ERROR,
	USBH_URBSTATUS_TIMEOUT,
	USBH_URBSTATUS_CANCELLED,
	USBH_URBSTATUS_STALL,
	USBH_URBSTATUS_DISCONNECTED,
	USBH_URBSTATUS_OK,
};

/*===========================================================================*/
/* Driver data structures and types.                                         */
/*===========================================================================*/

/* forward declarations */
typedef struct USBHDriver USBHDriver;
typedef struct usbh_port usbh_port_t;
typedef	struct usbh_device usbh_device_t;
typedef struct usbh_ep usbh_ep_t;
typedef struct usbh_urb usbh_urb_t;
typedef struct usbh_baseclassdriver usbh_baseclassdriver_t;
typedef struct usbh_classdriverinfo usbh_classdriverinfo_t;
#if HAL_USBH_USE_HUB
typedef struct USBHHubDriver USBHHubDriver;
#endif

/* typedefs */
typedef enum usbh_status usbh_status_t;
typedef enum usbh_devspeed usbh_devspeed_t;
typedef enum usbh_devstatus usbh_devstatus_t;
typedef enum usbh_epdir usbh_epdir_t;
typedef enum usbh_eptype usbh_eptype_t;
typedef enum usbh_epstatus usbh_epstatus_t;
typedef enum usbh_urbstatus usbh_urbstatus_t;
typedef uint16_t usbh_portstatus_t;
typedef uint16_t usbh_portcstatus_t;
typedef void (*usbh_completion_cb)(usbh_urb_t *);

/* include the low level driver; the required definitions are above */
#include "hal_usbh_lld.h"

#define USBH_DEFINE_BUFFER(var)	USBH_LLD_DEFINE_BUFFER(var)
#define USBH_DECLARE_STRUCT_MEMBER(member) USBH_LLD_DECLARE_STRUCT_MEMBER(member)

struct usbh_urb {
	usbh_ep_t *ep;

	void *userData;
	usbh_completion_cb callback;

	const void *setup_buff;
	void *buff;
	uint32_t requestedLength;
	uint32_t actualLength;

	usbh_urbstatus_t status;

	thread_reference_t waitingThread;
	thread_reference_t abortingThread;

	/* Low level part */
	_usbh_urb_ll_data
};

struct usbh_ep {
	usbh_device_t		*device;
	usbh_ep_t	 		*next;

	usbh_epstatus_t		status;
	uint8_t 			address;
	bool				in;
	usbh_eptype_t		type;
	uint16_t			wMaxPacketSize;
	uint8_t				bInterval;

	/* debug */
	const char			*name;

	/* Low-level part */
	_usbh_ep_ll_data
};

struct usbh_device {
	USBHDriver *host;	/* shortcut to host */

	usbh_ep_t ctrl;
	usbh_ep_t *endpoints;

	usbh_baseclassdriver_t *drivers;

	uint16_t langID0;

	usbh_devstatus_t status;
	usbh_devspeed_t speed;

	USBH_DECLARE_STRUCT_MEMBER(usbh_device_descriptor_t devDesc);
	USBH_DECLARE_STRUCT_MEMBER(usbh_config_descriptor_t basicConfigDesc);

	uint8_t *fullConfigurationDescriptor;
	uint8_t keepFullCfgDesc;

	uint8_t address;
	uint8_t bConfiguration;

	/* Low level part */
	_usbh_device_ll_data
};


struct usbh_port {
#if HAL_USBH_USE_HUB
	USBHHubDriver *hub;
#endif

	usbh_portstatus_t status;
	usbh_portcstatus_t c_status;

	usbh_port_t *next;

	uint8_t number;

	usbh_device_t device;

	/* Low level part */
	_usbh_port_ll_data
};

struct USBHDriver {
	usbh_status_t status;
	uint8_t address_bitmap[(USBH_MAX_ADDRESSES + 7) / 8];

	usbh_port_t rootport;

#if HAL_USBH_USE_HUB
	struct list_head hubs;
#endif

	/* Low level part */
	_usbhdriver_ll_data

#if USBH_DEBUG_ENABLE
	/* debug */
	uint8_t dbg_buff[USBH_DEBUG_BUFFER];
	THD_WORKING_AREA(waDebug, 512);
	input_queue_t iq;
#endif
};



/*===========================================================================*/
/* External declarations.                                                    */
/*===========================================================================*/


/*===========================================================================*/
/* Main driver API.		                                                     */
/*===========================================================================*/

#ifdef __cplusplus
extern "C" {
#endif

	/* Main functions */
	void usbhObjectInit(USBHDriver *usbh);
	void usbhInit(void);
	void usbhStart(USBHDriver *usbh);
	void usbhStop(USBHDriver *usbh);
	void usbhSuspend(USBHDriver *usbh);
	void usbhResume(USBHDriver *usbh);

	/* Device-related */
#if	USBH_DEBUG_ENABLE && USBH_DEBUG_ENABLE_INFO
	void usbhDevicePrintInfo(usbh_device_t *dev);
	void usbhDevicePrintConfiguration(const uint8_t *descriptor, uint16_t rem);
#else
#	define usbhDevicePrintInfo(dev) do {} while(0)
#	define usbhDevicePrintConfiguration(descriptor, rem) do {} while(0)
#endif
	bool usbhDeviceReadString(usbh_device_t *dev, char *dest, uint8_t size,
			uint8_t index, uint16_t langID);
	static inline usbh_port_t *usbhDeviceGetPort(usbh_device_t *dev) {
		return container_of(dev, usbh_port_t, device);
	}

	/* Synchronous API */
	usbh_urbstatus_t usbhSynchronousTransfer(usbh_ep_t *ep,
			void *data,
			uint32_t len,
			uint32_t *actual_len,
			systime_t timeout);

	static inline usbh_urbstatus_t usbhBulkTransfer(usbh_ep_t *ep,
			void *data,
			uint32_t len,
			uint32_t *actual_len,
			systime_t timeout) {
		osalDbgAssert(ep->type == USBH_EPTYPE_BULK, "wrong ep");

		return usbhSynchronousTransfer(ep, data, len, actual_len, timeout);
	}

	usbh_urbstatus_t usbhControlRequest(usbh_device_t *dev,
			uint8_t bmRequestType,
			uint8_t bRequest,
			uint16_t wValue,
			uint16_t wIndex,
			uint16_t wLength,
			uint8_t *buff);
	usbh_urbstatus_t usbhControlRequestExtended(usbh_device_t *dev,
			const usbh_control_request_t *req,
			uint8_t *buff,
			uint32_t *actual_len,
			systime_t timeout);

	/* Standard request helpers */
	bool usbhStdReqGetDeviceDescriptor(usbh_device_t *dev,
			uint16_t wLength,
			uint8_t *buf);
	bool usbhStdReqGetConfigurationDescriptor(usbh_device_t *dev,
			uint8_t index,
			uint16_t wLength,
			uint8_t *buf);
	bool usbhStdReqGetStringDescriptor(usbh_device_t *dev,
			uint8_t index,
			uint16_t langID,
			uint16_t wLength,
			uint8_t *buf);
	bool usbhStdReqSetInterface(usbh_device_t *dev,
			uint8_t bInterfaceNumber,
			uint8_t bAlternateSetting);
	bool usbhStdReqGetInterface(usbh_device_t *dev,
			uint8_t bInterfaceNumber,
			uint8_t *bAlternateSetting);

	/* Endpoint/pipe management */
	void usbhEPObjectInit(usbh_ep_t *ep, usbh_device_t *dev, const usbh_endpoint_descriptor_t *desc);
	static inline void usbhEPOpen(usbh_ep_t *ep) {
		osalDbgCheck(ep != 0);
		osalSysLock();
		osalDbgAssert(ep->status == USBH_EPSTATUS_CLOSED, "invalid state");
		usbh_lld_ep_open(ep);
		ep->next = ep->device->endpoints;
		ep->device->endpoints = ep;
		osalSysUnlock();
	}
	static inline void usbhEPCloseS(usbh_ep_t *ep) {
		osalDbgCheck(ep != 0);
		osalDbgCheckClassS();
		osalDbgAssert(ep->status != USBH_EPSTATUS_UNINITIALIZED, "invalid state");
		if (ep->status == USBH_EPSTATUS_CLOSED)
			return;
		usbh_lld_ep_close(ep);
	}
	static inline void usbhEPClose(usbh_ep_t *ep) {
		osalSysLock();
		usbhEPCloseS(ep);
		osalSysUnlock();
	}
	bool usbhEPReset(usbh_ep_t *ep);
	static inline bool usbhEPIsPeriodic(usbh_ep_t *ep) {
		osalDbgCheck(ep != NULL);
		return (ep->type & 1) != 0;
	}
	static inline bool usbhURBIsBusy(usbh_urb_t *urb) {
		osalDbgCheck(urb != NULL);
		return (urb->status == USBH_URBSTATUS_PENDING);
	}
	static inline void usbhEPSetName(usbh_ep_t *ep, const char *name) {
		ep->name = name;
	}

	/* URB management */
	void usbhURBObjectInit(usbh_urb_t *urb, usbh_ep_t *ep, usbh_completion_cb callback,
			void *user, void *buff, uint32_t len);
	void usbhURBObjectResetI(usbh_urb_t *urb);
	void usbhURBSubmitI(usbh_urb_t *urb);
	bool usbhURBCancelI(usbh_urb_t *urb);
	msg_t usbhURBSubmitAndWaitS(usbh_urb_t *urb, systime_t timeout);
	void usbhURBCancelAndWaitS(usbh_urb_t *urb);
	msg_t usbhURBWaitTimeoutS(usbh_urb_t *urb, systime_t timeout);

	static inline void usbhURBSubmit(usbh_urb_t *urb) {
		osalSysLock();
		usbhURBSubmitI(urb);
		osalOsRescheduleS();
		osalSysUnlock();
	}

	static inline bool usbhURBCancel(usbh_urb_t *urb) {
		bool ret;
		osalSysLock();
		ret = usbhURBCancelI(urb);
		osalOsRescheduleS();
		osalSysUnlock();
		return ret;
	}

	/* Main loop */
	void usbhMainLoop(USBHDriver *usbh);

#ifdef __cplusplus
}
#endif


/*===========================================================================*/
/* Class driver definitions and API.                                         */
/*===========================================================================*/

typedef struct usbh_classdriver_vmt usbh_classdriver_vmt_t;
struct usbh_classdriver_vmt {
	void (*init)(void);
	usbh_baseclassdriver_t *(*load)(usbh_device_t *dev,	const uint8_t *descriptor, uint16_t rem);
	void (*unload)(usbh_baseclassdriver_t *drv);
	/* TODO: add power control, suspend, etc */
};

struct usbh_classdriverinfo {
	const char *name;
	const usbh_classdriver_vmt_t *vmt;
};

#define _usbh_base_classdriver_data		\
	const usbh_classdriverinfo_t *info;	\
	usbh_device_t *dev;					\
	usbh_baseclassdriver_t *next;

struct usbh_baseclassdriver {
	_usbh_base_classdriver_data
};

#endif

#endif /* HAL_USBH_H_ */