usbh_cp210x.c 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507
  1. /*
  2. * Copyright (c) 2024 ~ 2025, sakumisu
  3. *
  4. * SPDX-License-Identifier: Apache-2.0
  5. */
  6. #include "usbh_core.h"
  7. #include "usbh_serial.h"
  8. #include "usbh_cp210x.h"
  9. #undef USB_DBG_TAG
  10. #define USB_DBG_TAG "usbh_cp210x"
  11. #include "usb_log.h"
  12. struct usbh_cp210x {
  13. uint8_t partnum;
  14. uint32_t fw_version;
  15. uint32_t min_speed;
  16. uint32_t max_speed;
  17. bool use_actual_rate;
  18. bool no_flow_control;
  19. bool no_event_mode;
  20. };
  21. struct cp210x_rate {
  22. uint32_t rate;
  23. uint32_t high;
  24. };
  25. static const struct cp210x_rate cp210x_an205_table1[] = {
  26. { 300, 300 },
  27. { 600, 600 },
  28. { 1200, 1200 },
  29. { 1800, 1800 },
  30. { 2400, 2400 },
  31. { 4000, 4000 },
  32. { 4800, 4803 },
  33. { 7200, 7207 },
  34. { 9600, 9612 },
  35. { 14400, 14428 },
  36. { 16000, 16062 },
  37. { 19200, 19250 },
  38. { 28800, 28912 },
  39. { 38400, 38601 },
  40. { 51200, 51558 },
  41. { 56000, 56280 },
  42. { 57600, 58053 },
  43. { 64000, 64111 },
  44. { 76800, 77608 },
  45. { 115200, 117028 },
  46. { 128000, 129347 },
  47. { 153600, 156868 },
  48. { 230400, 237832 },
  49. { 250000, 254234 },
  50. { 256000, 273066 },
  51. { 460800, 491520 },
  52. { 500000, 567138 },
  53. { 576000, 670254 },
  54. { 921600, 0xffffffff }
  55. };
  56. /*
  57. * Quantises the baud rate as per AN205 Table 1
  58. */
  59. static uint32_t cp210x_get_an205_rate(uint32_t baud)
  60. {
  61. int i;
  62. for (i = 0; i < ARRAY_SIZE(cp210x_an205_table1); ++i) {
  63. if (baud <= cp210x_an205_table1[i].high)
  64. break;
  65. }
  66. return cp210x_an205_table1[i].rate;
  67. }
  68. static uint32_t cp210x_get_actual_rate(uint32_t baud)
  69. {
  70. unsigned int prescale = 1;
  71. unsigned int div;
  72. if (baud <= 365)
  73. prescale = 4;
  74. div = DIV_ROUND_CLOSEST(48000000, 2 * prescale * baud);
  75. baud = 48000000 / (2 * prescale * div);
  76. return baud;
  77. }
  78. static void usbh_cp210x_init_max_speed(struct usbh_serial *serial)
  79. {
  80. struct usbh_cp210x *cp210x_class;
  81. if (!serial || !serial->hport || !serial->priv) {
  82. return;
  83. }
  84. cp210x_class = (struct usbh_cp210x *)serial->priv;
  85. bool use_actual_rate = false;
  86. uint32_t min = 300;
  87. uint32_t max;
  88. switch (cp210x_class->partnum) {
  89. case CP210X_PARTNUM_CP2101:
  90. max = 921600;
  91. break;
  92. case CP210X_PARTNUM_CP2102:
  93. case CP210X_PARTNUM_CP2103:
  94. max = 1000000;
  95. break;
  96. case CP210X_PARTNUM_CP2104:
  97. use_actual_rate = true;
  98. max = 2000000;
  99. break;
  100. case CP210X_PARTNUM_CP2108:
  101. max = 2000000;
  102. break;
  103. case CP210X_PARTNUM_CP2105:
  104. if (serial->intf == 0) {
  105. use_actual_rate = true;
  106. max = 2000000; /* ECI */
  107. } else {
  108. min = 2400;
  109. max = 921600; /* SCI */
  110. }
  111. break;
  112. case CP210X_PARTNUM_CP2102N_QFN28:
  113. case CP210X_PARTNUM_CP2102N_QFN24:
  114. case CP210X_PARTNUM_CP2102N_QFN20:
  115. use_actual_rate = true;
  116. max = 3000000;
  117. break;
  118. default:
  119. max = 2000000;
  120. break;
  121. }
  122. cp210x_class->min_speed = min;
  123. cp210x_class->max_speed = max;
  124. cp210x_class->use_actual_rate = use_actual_rate;
  125. }
  126. static int usbh_cp210x_control_out(struct usbh_serial *serial, uint8_t bRequest, uint16_t wValue, uint16_t wIndex, uint8_t *data, uint16_t size)
  127. {
  128. struct usb_setup_packet *setup;
  129. if (!serial || !serial->hport) {
  130. return -USB_ERR_INVAL;
  131. }
  132. setup = serial->hport->setup;
  133. setup->bmRequestType = USB_REQUEST_DIR_OUT | USB_REQUEST_VENDOR | USB_REQUEST_RECIPIENT_DEVICE;
  134. setup->bRequest = bRequest;
  135. setup->wValue = wValue;
  136. setup->wIndex = wIndex;
  137. setup->wLength = size;
  138. if (data && size) {
  139. memcpy(serial->iobuffer, data, size);
  140. return usbh_control_transfer(serial->hport, setup, serial->iobuffer);
  141. } else {
  142. return usbh_control_transfer(serial->hport, setup, NULL);
  143. }
  144. }
  145. static int usbh_cp210x_control_in(struct usbh_serial *serial, uint8_t bRequest, uint16_t wValue, uint16_t wIndex, uint8_t *data, uint16_t size)
  146. {
  147. struct usb_setup_packet *setup;
  148. int ret;
  149. if (!serial || !serial->hport) {
  150. return -USB_ERR_INVAL;
  151. }
  152. setup = serial->hport->setup;
  153. setup->bmRequestType = USB_REQUEST_DIR_IN | USB_REQUEST_VENDOR | USB_REQUEST_RECIPIENT_DEVICE;
  154. setup->bRequest = bRequest;
  155. setup->wValue = wValue;
  156. setup->wIndex = wIndex;
  157. setup->wLength = size;
  158. ret = usbh_control_transfer(serial->hport, setup, serial->iobuffer);
  159. if (ret < 0) {
  160. return ret;
  161. }
  162. memcpy(data, serial->iobuffer, size);
  163. return ret;
  164. }
  165. static int usbh_cp210x_get_partnum(struct usbh_serial *serial)
  166. {
  167. uint8_t version[3];
  168. struct usbh_cp210x *cp210x_class;
  169. int ret;
  170. if (!serial || !serial->hport || !serial->priv) {
  171. return -USB_ERR_INVAL;
  172. }
  173. cp210x_class = (struct usbh_cp210x *)serial->priv;
  174. ret = usbh_cp210x_control_in(serial, CP210X_VENDOR_SPECIFIC, CP210X_GET_PARTNUM, serial->intf, (uint8_t *)&cp210x_class->partnum, 1);
  175. if (ret < 0) {
  176. return ret;
  177. }
  178. USB_LOG_INFO("chip partnum: 0x%02x\r\n", cp210x_class->partnum);
  179. switch (cp210x_class->partnum) {
  180. case CP210X_PARTNUM_CP2102:
  181. break;
  182. case CP210X_PARTNUM_CP2105:
  183. case CP210X_PARTNUM_CP2108:
  184. ret = usbh_cp210x_control_in(serial, CP210X_VENDOR_SPECIFIC, CP210X_GET_FW_VER_2N, serial->intf, version, 3);
  185. if (ret < 0) {
  186. return ret;
  187. }
  188. cp210x_class->fw_version = version[0] << 16 | version[1] << 8 | version[2];
  189. break;
  190. case CP210X_PARTNUM_CP2102N_QFN28:
  191. case CP210X_PARTNUM_CP2102N_QFN24:
  192. case CP210X_PARTNUM_CP2102N_QFN20:
  193. ret = usbh_cp210x_control_in(serial, CP210X_VENDOR_SPECIFIC, CP210X_GET_FW_VER_2N, serial->intf, version, 3);
  194. if (ret < 0) {
  195. return ret;
  196. }
  197. cp210x_class->fw_version = version[0] << 16 | version[1] << 8 | version[2];
  198. if (cp210x_class->fw_version <= 0x10004)
  199. cp210x_class->no_flow_control = true;
  200. break;
  201. default:
  202. break;
  203. }
  204. return ret;
  205. }
  206. static int usbh_cp210x_enable(struct usbh_serial *serial)
  207. {
  208. return usbh_cp210x_control_out(serial, CP210X_IFC_ENABLE, 1, serial->intf, NULL, 0);
  209. }
  210. static int usbh_cp210x_set_chars(struct usbh_serial *serial)
  211. {
  212. struct cp210x_special_chars chars = { 0 };
  213. return usbh_cp210x_control_out(serial, CP210X_SET_CHARS, 0, serial->intf, (uint8_t *)&chars, sizeof(struct cp210x_special_chars));
  214. }
  215. // static int usbh_cp210x_get_common_status(struct usbh_serial *serial, struct cp210x_comm_status *status)
  216. // {
  217. // return usbh_cp210x_control_in(serial, CP210X_GET_COMM_STATUS, 0, serial->intf, (uint8_t *)status, sizeof(struct cp210x_comm_status));
  218. // }
  219. static int usbh_cp210x_set_baudrate(struct usbh_serial *serial, uint32_t baudrate)
  220. {
  221. struct usb_setup_packet *setup;
  222. struct usbh_cp210x *cp210x_class;
  223. if (!serial || !serial->hport || !serial->priv) {
  224. return -USB_ERR_INVAL;
  225. }
  226. setup = serial->hport->setup;
  227. cp210x_class = (struct usbh_cp210x *)serial->priv;
  228. setup->bmRequestType = USB_REQUEST_DIR_OUT | USB_REQUEST_VENDOR | USB_REQUEST_RECIPIENT_INTERFACE;
  229. setup->bRequest = CP210X_SET_BAUDRATE;
  230. setup->wValue = 0;
  231. setup->wIndex = serial->intf;
  232. setup->wLength = 4;
  233. if (cp210x_class->use_actual_rate)
  234. baudrate = cp210x_get_actual_rate(baudrate);
  235. else if (baudrate < 1000000)
  236. baudrate = cp210x_get_an205_rate(baudrate);
  237. memcpy(serial->iobuffer, (uint8_t *)&baudrate, 4);
  238. return usbh_control_transfer(serial->hport, setup, serial->iobuffer);
  239. }
  240. static int usbh_cp210x_set_data_format(struct usbh_serial *serial, uint8_t databits, uint8_t parity, uint8_t stopbits)
  241. {
  242. struct usb_setup_packet *setup;
  243. uint16_t value;
  244. if (!serial || !serial->hport) {
  245. return -USB_ERR_INVAL;
  246. }
  247. setup = serial->hport->setup;
  248. value = ((databits & 0x0F) << 8) | ((parity & 0x0f) << 4) | ((stopbits & 0x03) << 0);
  249. setup->bmRequestType = USB_REQUEST_DIR_OUT | USB_REQUEST_VENDOR | USB_REQUEST_RECIPIENT_INTERFACE;
  250. setup->bRequest = CP210X_SET_LINE_CTL;
  251. setup->wValue = value;
  252. setup->wIndex = serial->intf;
  253. setup->wLength = 0;
  254. return usbh_control_transfer(serial->hport, setup, NULL);
  255. }
  256. static int usbh_cp210x_attach(struct usbh_serial *serial)
  257. {
  258. int ret;
  259. struct usbh_cp210x *cp210x_class = usb_osal_malloc(sizeof(struct usbh_cp210x));
  260. if (!cp210x_class) {
  261. return -USB_ERR_NOMEM;
  262. }
  263. memset(cp210x_class, 0, sizeof(struct usbh_cp210x));
  264. serial->priv = cp210x_class;
  265. ret = usbh_cp210x_get_partnum(serial);
  266. usbh_cp210x_init_max_speed(serial);
  267. ret |= usbh_cp210x_enable(serial);
  268. ret |= usbh_cp210x_set_chars(serial);
  269. if (ret < 0) {
  270. goto errout;
  271. }
  272. return 0;
  273. errout:
  274. serial->priv = NULL;
  275. usb_osal_free(cp210x_class);
  276. return ret;
  277. }
  278. static void usbh_cp210x_detach(struct usbh_serial *serial)
  279. {
  280. if (serial && serial->priv) {
  281. serial->priv = NULL;
  282. usb_osal_free(serial->priv);
  283. }
  284. }
  285. int usbh_cp210x_set_flow_ctrl(struct usbh_serial *serial, bool enable)
  286. {
  287. struct cp210x_flow_ctl flow_ctl = { 0 };
  288. uint32_t flow_repl;
  289. uint32_t ctl_hs;
  290. int ret;
  291. ret = usbh_cp210x_control_in(serial, CP210X_GET_FLOW, 0, serial->intf, (uint8_t *)&flow_ctl, sizeof(struct cp210x_flow_ctl));
  292. if (ret < 0) {
  293. return ret;
  294. }
  295. ctl_hs = flow_ctl.lControlHandshake;
  296. flow_repl = flow_ctl.lFlowReplace;
  297. ctl_hs &= ~CP210X_SERIAL_DSR_HANDSHAKE;
  298. ctl_hs &= ~CP210X_SERIAL_DCD_HANDSHAKE;
  299. ctl_hs &= ~CP210X_SERIAL_DSR_SENSITIVITY;
  300. ctl_hs &= ~CP210X_SERIAL_DTR_MASK;
  301. ctl_hs |= CP210X_SERIAL_DTR_INACTIVE;
  302. flow_repl &= ~CP210X_SERIAL_RTS_MASK;
  303. flow_repl &= ~CP210X_SERIAL_AUTO_RECEIVE;
  304. flow_repl &= ~CP210X_SERIAL_AUTO_TRANSMIT;
  305. flow_repl |= CP210X_SERIAL_RTS_INACTIVE;
  306. flow_repl &= ~CP210X_SERIAL_RTS_MASK;
  307. if (enable) {
  308. ctl_hs |= CP210X_SERIAL_CTS_HANDSHAKE;
  309. } else {
  310. ctl_hs &= ~CP210X_SERIAL_CTS_HANDSHAKE;
  311. }
  312. flow_ctl.lControlHandshake = ctl_hs;
  313. flow_ctl.lFlowReplace = flow_repl;
  314. return usbh_cp210x_control_out(serial, CP210X_SET_FLOW, 0, serial->intf, (uint8_t *)&flow_ctl, sizeof(struct cp210x_flow_ctl));
  315. }
  316. int usbh_cp210x_set_line_coding(struct usbh_serial *serial, struct cdc_line_coding *line_coding)
  317. {
  318. int ret;
  319. ret = usbh_cp210x_set_baudrate(serial, line_coding->dwDTERate);
  320. if (ret < 0) {
  321. return ret;
  322. }
  323. return usbh_cp210x_set_data_format(serial, line_coding->bDataBits, line_coding->bParityType, line_coding->bCharFormat);
  324. }
  325. int usbh_cp210x_set_line_state(struct usbh_serial *serial, bool dtr, bool rts)
  326. {
  327. struct cp210x_flow_ctl flow_ctl = { 0 };
  328. uint32_t flow_repl;
  329. uint32_t ctl_hs;
  330. uint16_t control = 0;
  331. int ret;
  332. if (!serial || !serial->hport || !serial->priv) {
  333. return -USB_ERR_INVAL;
  334. }
  335. if (serial->rtscts) {
  336. ret = usbh_cp210x_control_in(serial, CP210X_GET_FLOW, 0, serial->intf, (uint8_t *)&flow_ctl, sizeof(struct cp210x_flow_ctl));
  337. if (ret < 0) {
  338. return ret;
  339. }
  340. ctl_hs = flow_ctl.lControlHandshake;
  341. flow_repl = flow_ctl.lFlowReplace;
  342. ctl_hs &= ~CP210X_SERIAL_DTR_MASK;
  343. if (dtr)
  344. ctl_hs |= CP210X_SERIAL_DTR_ACTIVE;
  345. else
  346. ctl_hs |= CP210X_SERIAL_DTR_INACTIVE;
  347. flow_repl &= ~CP210X_SERIAL_RTS_MASK;
  348. if (rts)
  349. flow_repl |= CP210X_SERIAL_RTS_FLOW_CTL;
  350. else
  351. flow_repl |= CP210X_SERIAL_RTS_INACTIVE;
  352. flow_ctl.lControlHandshake = ctl_hs;
  353. flow_ctl.lFlowReplace = flow_repl;
  354. return usbh_cp210x_control_out(serial, CP210X_SET_FLOW, 0, serial->intf, (uint8_t *)&flow_ctl, sizeof(struct cp210x_flow_ctl));
  355. } else {
  356. if (dtr) {
  357. control |= CP210X_CONTROL_DTR;
  358. }
  359. if (rts) {
  360. control |= CP210X_CONTROL_RTS;
  361. }
  362. control |= CP210X_CONTROL_WRITE_DTR;
  363. control |= CP210X_CONTROL_WRITE_RTS;
  364. return usbh_cp210x_control_out(serial, CP210X_SET_MHS, control, serial->intf, NULL, 0);
  365. }
  366. }
  367. static int usbh_cp210x_get_modem_status(struct usbh_serial *serial)
  368. {
  369. int ret;
  370. uint8_t control;
  371. uint16_t status;
  372. if (!serial || !serial->hport) {
  373. return -USB_ERR_INVAL;
  374. }
  375. ret = usbh_cp210x_control_in(serial, CP210X_GET_MDMSTS, 0, serial->intf, (uint8_t *)&control, 1);
  376. if (ret < 0) {
  377. return ret;
  378. }
  379. status = ((control & CP210X_CONTROL_DTR) ? USBH_SERIAL_TIOCM_DTR : 0) |
  380. ((control & CP210X_CONTROL_RTS) ? USBH_SERIAL_TIOCM_RTS : 0) |
  381. ((control & CP210X_CONTROL_CTS) ? USBH_SERIAL_TIOCM_CTS : 0) |
  382. ((control & CP210X_CONTROL_DSR) ? USBH_SERIAL_TIOCM_DSR : 0) |
  383. ((control & CP210X_CONTROL_RING) ? USBH_SERIAL_TIOCM_RI : 0) |
  384. ((control & CP210X_CONTROL_DCD) ? USBH_SERIAL_TIOCM_CD : 0);
  385. return status;
  386. }
  387. static const struct usbh_serial_driver cp210x_driver = {
  388. .driver_name = "cp210x",
  389. .ignore_rx_header = 0,
  390. .ignore_tx_header = 0,
  391. .attach = usbh_cp210x_attach,
  392. .detach = usbh_cp210x_detach,
  393. .set_flow_control = usbh_cp210x_set_flow_ctrl,
  394. .set_line_coding = usbh_cp210x_set_line_coding,
  395. .get_line_coding = NULL,
  396. .set_line_state = usbh_cp210x_set_line_state,
  397. .get_modem_status = usbh_cp210x_get_modem_status,
  398. };
  399. static int usbh_cp210x_connect(struct usbh_hubport *hport, uint8_t intf)
  400. {
  401. return usbh_serial_probe(hport, intf, &cp210x_driver) ? 0 : -USB_ERR_NOMEM;
  402. }
  403. static int usbh_cp210x_disconnect(struct usbh_hubport *hport, uint8_t intf)
  404. {
  405. struct usbh_serial *serial = (struct usbh_serial *)hport->config.intf[intf].priv;
  406. if (serial) {
  407. usbh_serial_remove(serial);
  408. }
  409. return 0;
  410. }
  411. static const uint16_t cp210x_id_table[][2] = {
  412. { 0x10C4, 0xEA60 },
  413. { 0, 0 },
  414. };
  415. const struct usbh_class_driver cp210x_class_driver = {
  416. .driver_name = "cp210x",
  417. .connect = usbh_cp210x_connect,
  418. .disconnect = usbh_cp210x_disconnect
  419. };
  420. CLASS_INFO_DEFINE const struct usbh_class_info cp210x_class_info = {
  421. .match_flags = USB_CLASS_MATCH_VID_PID | USB_CLASS_MATCH_INTF_CLASS,
  422. .bInterfaceClass = 0xff,
  423. .bInterfaceSubClass = 0x00,
  424. .bInterfaceProtocol = 0x00,
  425. .id_table = cp210x_id_table,
  426. .class_driver = &cp210x_class_driver
  427. };