1 /*
2 * Copyright (c) 2022, sakumisu
3 *
4 * SPDX-License-Identifier: Apache-2.0
5 */
6 #include "usbh_core.h"
7 #include "usbh_cdc_acm.h"
8
9 #define DEV_FORMAT "/dev/ttyACM%d"
10
11 USB_NOCACHE_RAM_SECTION USB_MEM_ALIGNX struct cdc_line_coding g_cdc_line_coding;
12
13 static struct usbh_cdc_acm g_cdc_acm_class[CONFIG_USBHOST_MAX_CDC_ACM_CLASS];
14 static uint32_t g_devinuse = 0;
15
usbh_cdc_acm_class_alloc(void)16 static struct usbh_cdc_acm *usbh_cdc_acm_class_alloc(void)
17 {
18 int devno;
19
20 for (devno = 0; devno < CONFIG_USBHOST_MAX_CDC_ACM_CLASS; devno++) {
21 if ((g_devinuse & (1 << devno)) == 0) {
22 g_devinuse |= (1 << devno);
23 memset(&g_cdc_acm_class[devno], 0, sizeof(struct usbh_cdc_acm));
24 g_cdc_acm_class[devno].minor = devno;
25 return &g_cdc_acm_class[devno];
26 }
27 }
28 return NULL;
29 }
30
usbh_cdc_acm_class_free(struct usbh_cdc_acm * cdc_acm_class)31 static void usbh_cdc_acm_class_free(struct usbh_cdc_acm *cdc_acm_class)
32 {
33 int devno = cdc_acm_class->minor;
34
35 if (devno >= 0 && devno < 32) {
36 g_devinuse &= ~(1 << devno);
37 }
38 memset(cdc_acm_class, 0, sizeof(struct usbh_cdc_acm));
39 }
40
usbh_cdc_acm_set_line_coding(struct usbh_cdc_acm * cdc_acm_class,struct cdc_line_coding * line_coding)41 int usbh_cdc_acm_set_line_coding(struct usbh_cdc_acm *cdc_acm_class, struct cdc_line_coding *line_coding)
42 {
43 struct usb_setup_packet *setup = cdc_acm_class->hport->setup;
44
45 setup->bmRequestType = USB_REQUEST_DIR_OUT | USB_REQUEST_CLASS | USB_REQUEST_RECIPIENT_INTERFACE;
46 setup->bRequest = CDC_REQUEST_SET_LINE_CODING;
47 setup->wValue = 0;
48 setup->wIndex = cdc_acm_class->ctrl_intf;
49 setup->wLength = 7;
50
51 memcpy((uint8_t *)&g_cdc_line_coding, line_coding, sizeof(struct cdc_line_coding));
52
53 return usbh_control_transfer(cdc_acm_class->hport->ep0, setup, (uint8_t *)&g_cdc_line_coding);
54 }
55
usbh_cdc_acm_get_line_coding(struct usbh_cdc_acm * cdc_acm_class,struct cdc_line_coding * line_coding)56 int usbh_cdc_acm_get_line_coding(struct usbh_cdc_acm *cdc_acm_class, struct cdc_line_coding *line_coding)
57 {
58 struct usb_setup_packet *setup = cdc_acm_class->hport->setup;
59 int ret;
60
61 setup->bmRequestType = USB_REQUEST_DIR_IN | USB_REQUEST_CLASS | USB_REQUEST_RECIPIENT_INTERFACE;
62 setup->bRequest = CDC_REQUEST_GET_LINE_CODING;
63 setup->wValue = 0;
64 setup->wIndex = cdc_acm_class->ctrl_intf;
65 setup->wLength = 7;
66
67 ret = usbh_control_transfer(cdc_acm_class->hport->ep0, setup, (uint8_t *)&g_cdc_line_coding);
68 if (ret < 0) {
69 return ret;
70 }
71 memcpy(line_coding, (uint8_t *)&g_cdc_line_coding, sizeof(struct cdc_line_coding));
72 return ret;
73 }
74
usbh_cdc_acm_set_line_state(struct usbh_cdc_acm * cdc_acm_class,bool dtr,bool rts)75 int usbh_cdc_acm_set_line_state(struct usbh_cdc_acm *cdc_acm_class, bool dtr, bool rts)
76 {
77 struct usb_setup_packet *setup = cdc_acm_class->hport->setup;
78
79 setup->bmRequestType = USB_REQUEST_DIR_OUT | USB_REQUEST_CLASS | USB_REQUEST_RECIPIENT_INTERFACE;
80 setup->bRequest = CDC_REQUEST_SET_CONTROL_LINE_STATE;
81 setup->wValue = (dtr << 0) | (rts << 1);
82 setup->wIndex = cdc_acm_class->ctrl_intf;
83 setup->wLength = 0;
84
85 cdc_acm_class->dtr = dtr;
86 cdc_acm_class->rts = rts;
87
88 return usbh_control_transfer(cdc_acm_class->hport->ep0, setup, NULL);
89 }
90
usbh_cdc_acm_connect(struct usbh_hubport * hport,uint8_t intf)91 static int usbh_cdc_acm_connect(struct usbh_hubport *hport, uint8_t intf)
92 {
93 struct usb_endpoint_descriptor *ep_desc;
94 int ret;
95
96 struct usbh_cdc_acm *cdc_acm_class = usbh_cdc_acm_class_alloc();
97 if (cdc_acm_class == NULL) {
98 USB_LOG_ERR("Fail to alloc cdc_acm_class\r\n");
99 return -ENOMEM;
100 }
101
102 cdc_acm_class->hport = hport;
103 cdc_acm_class->ctrl_intf = intf;
104 cdc_acm_class->data_intf = intf + 1;
105
106 hport->config.intf[intf].priv = cdc_acm_class;
107 hport->config.intf[intf + 1].priv = NULL;
108
109 cdc_acm_class->linecoding.dwDTERate = 115200;
110 cdc_acm_class->linecoding.bDataBits = 8;
111 cdc_acm_class->linecoding.bParityType = 0;
112 cdc_acm_class->linecoding.bCharFormat = 0;
113 ret = usbh_cdc_acm_set_line_coding(cdc_acm_class, &cdc_acm_class->linecoding);
114 if (ret < 0) {
115 USB_LOG_ERR("Fail to set linecoding\r\n");
116 return ret;
117 }
118
119 ret = usbh_cdc_acm_set_line_state(cdc_acm_class, true, true);
120 if (ret < 0) {
121 USB_LOG_ERR("Fail to set line state\r\n");
122 return ret;
123 }
124
125 #ifdef CONFIG_USBHOST_CDC_ACM_NOTIFY
126 ep_desc = &hport->config.intf[intf].altsetting[0].ep[0].ep_desc;
127 usbh_hport_activate_epx(&cdc_acm_class->intin, hport, ep_desc);
128 #endif
129 for (uint8_t i = 0; i < hport->config.intf[intf + 1].altsetting[0].intf_desc.bNumEndpoints; i++) {
130 ep_desc = &hport->config.intf[intf + 1].altsetting[0].ep[i].ep_desc;
131
132 if (ep_desc->bEndpointAddress & 0x80) {
133 usbh_hport_activate_epx(&cdc_acm_class->bulkin, hport, ep_desc);
134 } else {
135 usbh_hport_activate_epx(&cdc_acm_class->bulkout, hport, ep_desc);
136 }
137 }
138
139 snprintf(hport->config.intf[intf].devname, CONFIG_USBHOST_DEV_NAMELEN, DEV_FORMAT, cdc_acm_class->minor);
140
141 USB_LOG_INFO("Register CDC ACM Class:%s\r\n", hport->config.intf[intf].devname);
142
143 usbh_cdc_acm_run(cdc_acm_class);
144 return ret;
145 }
146
usbh_cdc_acm_disconnect(struct usbh_hubport * hport,uint8_t intf)147 static int usbh_cdc_acm_disconnect(struct usbh_hubport *hport, uint8_t intf)
148 {
149 int ret = 0;
150
151 struct usbh_cdc_acm *cdc_acm_class = (struct usbh_cdc_acm *)hport->config.intf[intf].priv;
152
153 if (cdc_acm_class) {
154 if (cdc_acm_class->bulkin) {
155 usbh_pipe_free(cdc_acm_class->bulkin);
156 }
157
158 if (cdc_acm_class->bulkout) {
159 usbh_pipe_free(cdc_acm_class->bulkout);
160 }
161
162 if (hport->config.intf[intf].devname[0] != '\0') {
163 USB_LOG_INFO("Unregister CDC ACM Class:%s\r\n", hport->config.intf[intf].devname);
164 usbh_cdc_acm_stop(cdc_acm_class);
165 }
166
167 usbh_cdc_acm_class_free(cdc_acm_class);
168 }
169
170 return ret;
171 }
172
usbh_cdc_data_connect(struct usbh_hubport * hport,uint8_t intf)173 static int usbh_cdc_data_connect(struct usbh_hubport *hport, uint8_t intf)
174 {
175 return 0;
176 }
177
usbh_cdc_data_disconnect(struct usbh_hubport * hport,uint8_t intf)178 static int usbh_cdc_data_disconnect(struct usbh_hubport *hport, uint8_t intf)
179 {
180 return 0;
181 }
182
usbh_cdc_acm_run(struct usbh_cdc_acm * cdc_acm_class)183 __WEAK void usbh_cdc_acm_run(struct usbh_cdc_acm *cdc_acm_class)
184 {
185 }
186
usbh_cdc_acm_stop(struct usbh_cdc_acm * cdc_acm_class)187 __WEAK void usbh_cdc_acm_stop(struct usbh_cdc_acm *cdc_acm_class)
188 {
189 }
190
191 const struct usbh_class_driver cdc_acm_class_driver = {
192 .driver_name = "cdc_acm",
193 .connect = usbh_cdc_acm_connect,
194 .disconnect = usbh_cdc_acm_disconnect
195 };
196
197 const struct usbh_class_driver cdc_data_class_driver = {
198 .driver_name = "cdc_data",
199 .connect = usbh_cdc_data_connect,
200 .disconnect = usbh_cdc_data_disconnect
201 };
202
203 CLASS_INFO_DEFINE const struct usbh_class_info cdc_acm_class_info = {
204 .match_flags = USB_CLASS_MATCH_INTF_CLASS | USB_CLASS_MATCH_INTF_SUBCLASS | USB_CLASS_MATCH_INTF_PROTOCOL,
205 .class = USB_DEVICE_CLASS_CDC,
206 .subclass = CDC_ABSTRACT_CONTROL_MODEL,
207 .protocol = CDC_COMMON_PROTOCOL_AT_COMMANDS,
208 .vid = 0x00,
209 .pid = 0x00,
210 .class_driver = &cdc_acm_class_driver
211 };
212
213 CLASS_INFO_DEFINE const struct usbh_class_info cdc_data_class_info = {
214 .match_flags = USB_CLASS_MATCH_INTF_CLASS,
215 .class = USB_DEVICE_CLASS_CDC_DATA,
216 .subclass = 0x00,
217 .protocol = 0x00,
218 .vid = 0x00,
219 .pid = 0x00,
220 .class_driver = &cdc_data_class_driver
221 };
222