• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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