fusb_generic_hub.c 8.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295
  1. /*
  2. * Copyright : (C) 2022 Phytium Information Technology, Inc.
  3. * All Rights Reserved.
  4. *
  5. * This program is OPEN SOURCE software: you can redistribute it and/or modify it
  6. * under the terms of the Phytium Public License as published by the Phytium Technology Co.,Ltd,
  7. * either version 1.0 of the License, or (at your option) any later version.
  8. *
  9. * This program is distributed in the hope that it will be useful,but WITHOUT ANY WARRANTY;
  10. * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
  11. * See the Phytium Public License for more details.
  12. *
  13. *
  14. * FilePath: fusb_generic_hub.c
  15. * Date: 2022-02-11 13:33:11
  16. * LastEditTime: 2022-02-18 09:19:27
  17. * Description:  This files is for implmentation of generic hub function
  18. *
  19. * Modify History:
  20. * Ver   Who        Date         Changes
  21. * ----- ------     --------    --------------------------------------
  22. * 1.0 Zhugengyu 2022/2/7 init commit
  23. */
  24. #include "fsleep.h"
  25. #include "fdebug.h"
  26. #include "fusb.h"
  27. #include "fusb_generic_hub.h"
  28. #define FUSB_DEBUG_TAG "FUSB_HUB"
  29. #define FUSB_ERROR(format, ...) FT_DEBUG_PRINT_E(FUSB_DEBUG_TAG, format, ##__VA_ARGS__)
  30. #define FUSB_WARN(format, ...) FT_DEBUG_PRINT_W(FUSB_DEBUG_TAG, format, ##__VA_ARGS__)
  31. #define FUSB_INFO(format, ...) FT_DEBUG_PRINT_I(FUSB_DEBUG_TAG, format, ##__VA_ARGS__)
  32. #define FUSB_DEBUG(format, ...) FT_DEBUG_PRINT_D(FUSB_DEBUG_TAG, format, ##__VA_ARGS__)
  33. void FUsbGenericHubDestory(FUsbDev *const dev)
  34. {
  35. FUsbGenericHub *const hub = FUSB_GEN_HUB_GET(dev);
  36. FUsb *instace = dev->controller->usb;
  37. if (!hub)
  38. return;
  39. /* First, detach all devices behind this hub */
  40. int port;
  41. for (port = 1; port <= hub->num_ports; ++port)
  42. {
  43. if (hub->ports[port] >= 0)
  44. {
  45. FUSB_INFO("generic_hub: Detachment at port %d ", port);
  46. FUsbDetachDev(dev->controller, hub->ports[port]);
  47. hub->ports[port] = FUSB_NO_DEV_ADDR;
  48. }
  49. }
  50. /* Disable all ports */
  51. if (hub->ops->disable_port)
  52. {
  53. for (port = 1; port <= hub->num_ports; ++port)
  54. hub->ops->disable_port(dev, port);
  55. }
  56. FUSB_FREE(instace, hub->ports); // free(hub->ports);
  57. FUSB_FREE(instace, hub); // free(hub);
  58. }
  59. static int FUsbGenericHubDebounce(FUsbDev *const dev, const int port)
  60. {
  61. FUsbGenericHub *const hub = FUSB_GEN_HUB_GET(dev);
  62. const int step_ms = 1; /* linux uses 25ms, we're busy anyway */
  63. const int at_least_ms = 100; /* 100ms as in usb20 spec 9.1.2 */
  64. const int timeout_ms = 1500; /* linux uses this value */
  65. int total_ms = 0;
  66. int stable_ms = 0;
  67. while (stable_ms < at_least_ms && total_ms < timeout_ms)
  68. {
  69. fsleep_millisec(step_ms);
  70. const int changed = hub->ops->port_status_changed(dev, port);
  71. const int connected = hub->ops->port_connected(dev, port);
  72. if (changed < 0 || connected < 0)
  73. return -1;
  74. if (!changed && connected)
  75. {
  76. stable_ms += step_ms;
  77. }
  78. else
  79. {
  80. FUSB_INFO("generic_hub: Unstable connection at %d ",
  81. port);
  82. stable_ms = 0;
  83. }
  84. total_ms += step_ms;
  85. }
  86. if (total_ms >= timeout_ms)
  87. FUSB_INFO("generic_hub: Debouncing timed out at %d ", port);
  88. return 0; /* ignore timeouts, try to always go on */
  89. }
  90. int FUsbGenericHubWaitForPort(FUsbDev *const dev, const int port,
  91. const int wait_for,
  92. int (*const port_op)(FUsbDev *, int),
  93. int timeout_steps, const int step_us)
  94. {
  95. int state;
  96. do
  97. {
  98. state = port_op(dev, port);
  99. if (state < 0)
  100. return -1;
  101. else if (!!state == wait_for)
  102. return timeout_steps;
  103. fsleep_microsec(step_us);
  104. --timeout_steps;
  105. }
  106. while (timeout_steps);
  107. return 0;
  108. }
  109. int FUsbGenericHubResetPort(FUsbDev *const dev, const int port)
  110. {
  111. FUsbGenericHub *const hub = FUSB_GEN_HUB_GET(dev);
  112. if (hub->ops->start_port_reset(dev, port) < 0)
  113. return -1;
  114. /* wait for 10ms (usb20 spec 11.5.1.5: reset should take 10 to 20ms) */
  115. fsleep_millisec(10);
  116. /* now wait 12ms for the hub to finish the reset */
  117. const int ret = FUsbGenericHubWaitForPort(
  118. /* time out after 120 * 100us = 12ms */
  119. dev, port, 0, hub->ops->port_in_reset, 120, 100);
  120. if (ret < 0)
  121. return -1;
  122. else if (!ret)
  123. FUSB_INFO("generic_hub: Reset timed out at port %d ", port);
  124. return 0; /* ignore timeouts, try to always go on */
  125. }
  126. static int FUsbGenericHubDetachDev(FUsbDev *const dev, const int port)
  127. {
  128. FUsbGenericHub *const hub = FUSB_GEN_HUB_GET(dev);
  129. FUsbDetachDev(dev->controller, hub->ports[port]);
  130. hub->ports[port] = FUSB_NO_DEV_ADDR;
  131. return 0;
  132. }
  133. static int FUsbGenericHubAttachDev(FUsbDev *const dev, const int port)
  134. {
  135. FUsbGenericHub *const hub = FUSB_GEN_HUB_GET(dev);
  136. if (FUsbGenericHubDebounce(dev, port) < 0)
  137. return -1;
  138. if (hub->ops->reset_port)
  139. {
  140. if (hub->ops->reset_port(dev, port) < 0)
  141. return -1;
  142. if (!hub->ops->port_connected(dev, port))
  143. {
  144. FUSB_INFO(
  145. "generic_hub: Port %d disconnected after "
  146. "reset. Possibly upgraded, rescan required. ",
  147. port);
  148. return 0;
  149. }
  150. /* after reset the port will be enabled automatically */
  151. const int ret = FUsbGenericHubWaitForPort(
  152. /* time out after 1,000 * 10us = 10ms */
  153. dev, port, 1, hub->ops->port_enabled, 1000, 10);
  154. if (ret < 0)
  155. return -1;
  156. else if (!ret)
  157. FUSB_INFO("generic_hub: Port %d still "
  158. "disabled after 10ms ",
  159. port);
  160. }
  161. const FUsbSpeed speed = hub->ops->port_speed(dev, port);
  162. if (speed >= 0)
  163. {
  164. FUSB_DEBUG("generic_hub: Success at port %d ", port);
  165. if (hub->ops->reset_port)
  166. fsleep_millisec(10); /* Reset recovery time
  167. (usb20 spec 7.1.7.5) */
  168. hub->ports[port] = FUsbAttachDev(
  169. dev->controller, dev->address, port, speed);
  170. }
  171. return 0;
  172. }
  173. int FUsbGenericHubScanPort(FUsbDev *const dev, const int port)
  174. {
  175. FUsbGenericHub *const hub = FUSB_GEN_HUB_GET(dev);
  176. if (hub->ports[port] >= 0)
  177. {
  178. FUSB_INFO("generic_hub: Detachment at port %d ", port);
  179. const int ret = FUsbGenericHubDetachDev(dev, port);
  180. if (ret < 0)
  181. return ret;
  182. }
  183. if (hub->ops->port_connected(dev, port))
  184. {
  185. FUSB_INFO("generic_hub: Attachment at port %d ", port);
  186. return FUsbGenericHubAttachDev(dev, port);
  187. }
  188. return 0;
  189. }
  190. static void FUsbGenericHubPoll(FUsbDev *const dev)
  191. {
  192. FUsbGenericHub *const hub = FUSB_GEN_HUB_GET(dev);
  193. if (!hub)
  194. return;
  195. if (hub->ops->hub_status_changed &&
  196. hub->ops->hub_status_changed(dev) != FUSB_CC_SUCCESS)
  197. {
  198. return;
  199. }
  200. int port;
  201. for (port = 1; port <= hub->num_ports; ++port)
  202. {
  203. const FUsbTransCode ret = hub->ops->port_status_changed(dev, port);
  204. if (ret < 0)
  205. {
  206. FUSB_WARN("Transcode %d", ret);
  207. return;
  208. }
  209. else if (ret == FUSB_CC_SUCCESS)
  210. {
  211. FUSB_INFO("generic_hub: Port change at %d ", port);
  212. if (FUsbGenericHubScanPort(dev, port) < 0)
  213. return;
  214. }
  215. }
  216. }
  217. int FUsbGenericHubInit(FUsbDev *const dev, const int num_ports,
  218. const FUsbGenericHubOps *const ops)
  219. {
  220. int port;
  221. FUsb *instance = dev->controller->usb;
  222. dev->destroy = FUsbGenericHubDestory;
  223. dev->poll = FUsbGenericHubPoll;
  224. FASSERT(NULL == dev->data);
  225. dev->data = FUSB_ALLOCATE(instance, sizeof(FUsbGenericHub), FUSB_DEFAULT_ALIGN);
  226. if (NULL == dev->data)
  227. {
  228. FUSB_ERROR("generic_hub: ERROR: Out of memory ");
  229. return -1;
  230. }
  231. FUsbGenericHub *const hub = FUSB_GEN_HUB_GET(dev);
  232. hub->num_ports = num_ports;
  233. FASSERT(NULL == hub->ports);
  234. hub->ports = FUSB_ALLOCATE(instance, sizeof(*hub->ports) * (num_ports + 1), FUSB_DEFAULT_ALIGN);
  235. hub->ops = ops;
  236. if (NULL == hub->ports)
  237. {
  238. FUSB_ERROR("generic_hub: ERROR: Out of memory ");
  239. FUSB_FREE(instance, dev->data);
  240. dev->data = NULL;
  241. return -1;
  242. }
  243. for (port = 1; port <= num_ports; ++port)
  244. hub->ports[port] = FUSB_NO_DEV_ADDR;
  245. /* Enable all ports */
  246. if (ops->enable_port)
  247. {
  248. for (port = 1; port <= num_ports; ++port)
  249. ops->enable_port(dev, port);
  250. /* wait once for all ports */
  251. fsleep_millisec(20);
  252. }
  253. return 0;
  254. }