summaryrefslogtreecommitdiff
path: root/chip/stm32/usb_hid.c
blob: e9426b690d964030aff3ac5ac3399d9349885612 (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
/* Copyright 2014 The ChromiumOS Authors
 * Use of this source code is governed by a BSD-style license that can be
 * found in the LICENSE file.
 */

#include "clock.h"
#include "common.h"
#include "config.h"
#include "console.h"
#include "gpio.h"
#include "hooks.h"
#include "link_defs.h"
#include "registers.h"
#include "task.h"
#include "timer.h"
#include "util.h"
#include "usb_descriptor.h"
#include "usb_hw.h"
#include "usb_hid.h"
#include "usb_hid_hw.h"

/* Console output macro */
#define CPRINTF(format, args...) cprintf(CC_USB, format, ##args)

void hid_tx(int ep)
{
	/* clear IT */
	STM32_USB_EP(ep) = (STM32_USB_EP(ep) & EP_MASK);
}

void hid_reset(int ep, usb_uint *hid_ep_tx_buf, int tx_len,
	       usb_uint *hid_ep_rx_buf, int rx_len)
{
	int i;
	uint16_t ep_reg;

	btable_ep[ep].tx_addr = usb_sram_addr(hid_ep_tx_buf);
	btable_ep[ep].tx_count = tx_len;

	/* STM32 USB SRAM needs to be accessed one U16 at a time */
	for (i = 0; i < DIV_ROUND_UP(tx_len, 2); i++)
		hid_ep_tx_buf[i] = 0;

	ep_reg = (ep << 0) /* Endpoint Address */ | EP_TX_VALID |
		 (3 << 9) /* interrupt EP */ | EP_RX_DISAB;

	/* Enable RX for output reports */
	if (hid_ep_rx_buf && rx_len > 0) {
		btable_ep[ep].rx_addr = usb_sram_addr(hid_ep_rx_buf);
		btable_ep[ep].rx_count = ((rx_len + 1) / 2) << 10;

		ep_reg |= EP_RX_VALID; /* RX Valid */
	}

	STM32_USB_EP(ep) = ep_reg;
}

/*
 * Keep track of state in case we need to be called multiple times,
 * if the report length is bigger than 64 bytes.
 */
static int report_left;
static const uint8_t *report_ptr;

/*
 * Send report through ep0_buf_tx.
 *
 * If report size is greater than USB packet size (64 bytes), rest of the
 * reports will be saved in `report_ptr` and `report_left`, so we can call this
 * function again to send the remain parts.
 *
 * @return 0 if entire report is sent, 1 if there are remaining data.
 */
static int send_report(usb_uint *ep0_buf_tx, const uint8_t *report,
		       int report_size)
{
	int packet_size = MIN(report_size, USB_MAX_PACKET_SIZE);

	memcpy_to_usbram((void *)usb_sram_addr(ep0_buf_tx), report,
			 packet_size);
	btable_ep[0].tx_count = packet_size;
	/* report_left != 0 if report doesn't fit in 1 packet. */
	report_left = report_size - packet_size;
	report_ptr = report + packet_size;

	STM32_TOGGLE_EP(0, EP_TX_RX_MASK, EP_TX_RX_VALID,
			report_left ? 0 : EP_STATUS_OUT);

	return report_left ? 1 : 0;
}

int hid_iface_request(usb_uint *ep0_buf_rx, usb_uint *ep0_buf_tx,
		      const struct usb_hid_config_t *config)
{
	const uint8_t *report_desc = config->report_desc;
	int report_size = config->report_size;
	const struct usb_hid_descriptor *hid_desc = config->hid_desc;

	if (!ep0_buf_rx) {
		/*
		 * Continue previous transfer. We ignore report_desc/size here,
		 * which is fine as only one GET_DESCRIPTOR command comes at a
		 * time.
		 */
		if (report_left == 0)
			return -1;
		report_size = MIN(USB_MAX_PACKET_SIZE, report_left);
		memcpy_to_usbram((void *)usb_sram_addr(ep0_buf_tx), report_ptr,
				 report_size);
		btable_ep[0].tx_count = report_size;
		report_left -= report_size;
		report_ptr += report_size;
		STM32_TOGGLE_EP(0, EP_TX_MASK, EP_TX_VALID,
				report_left ? 0 : EP_STATUS_OUT);
		return report_left ? 1 : 0;
	} else if (ep0_buf_rx[0] == (USB_DIR_IN | USB_RECIP_INTERFACE |
				     (USB_REQ_GET_DESCRIPTOR << 8))) {
		if (ep0_buf_rx[1] == (USB_HID_DT_REPORT << 8)) {
			/* Setup : HID specific : Get Report descriptor */
			return send_report(ep0_buf_tx, report_desc,
					   MIN(ep0_buf_rx[3], report_size));
		} else if (ep0_buf_rx[1] == (USB_HID_DT_HID << 8)) {
			/* Setup : HID specific : Get HID descriptor */
			memcpy_to_usbram_ep0_patch(hid_desc, sizeof(*hid_desc));
			btable_ep[0].tx_count = sizeof(*hid_desc);
			STM32_TOGGLE_EP(0, EP_TX_RX_MASK, EP_TX_RX_VALID,
					EP_STATUS_OUT);
			return 0;
		}
	} else if (ep0_buf_rx[0] ==
		   (USB_DIR_IN | USB_RECIP_INTERFACE | USB_TYPE_CLASS |
		    (USB_HID_REQ_GET_REPORT << 8))) {
		const uint8_t report_type = (ep0_buf_rx[1] >> 8) & 0xFF;
		const uint8_t report_id = ep0_buf_rx[1] & 0xFF;
		int retval;

		report_left = ep0_buf_rx[3];
		if (!config->get_report) /* not supported */
			return -1;

		retval = config->get_report(report_id, report_type, &report_ptr,
					    &report_left);
		if (retval)
			return retval;

		return send_report(ep0_buf_tx, report_ptr, report_left);
	}

	return -1;
}