選択できるのは25トピックまでです。 トピックは、先頭が英数字で、英数字とダッシュ('-')を使用した35文字以内のものにしてください。
 
 
 
 
 
 

654 行
17 KiB

  1. /*
  2. * This file is part of the libopencm3 project.
  3. *
  4. * Copyright (C) 2013 Alexandru Gagniuc <mr.nuke.me@gmail.com>
  5. *
  6. * This library is free software: you can redistribute it and/or modify
  7. * it under the terms of the GNU Lesser General Public License as published by
  8. * the Free Software Foundation, either version 3 of the License, or
  9. * (at your option) any later version.
  10. *
  11. * This library is distributed in the hope that it will be useful,
  12. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  13. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  14. * GNU Lesser General Public License for more details.
  15. *
  16. * You should have received a copy of the GNU Lesser General Public License
  17. * along with this library. If not, see <http://www.gnu.org/licenses/>.
  18. */
  19. /**
  20. * @defgroup usb_file USB
  21. *
  22. * @ingroup LM4Fxx
  23. *
  24. * @author @htmlonly &copy; @endhtmlonly 2013
  25. * Alexandru Gagniuc <mr.nuke.me@gmail.com>
  26. *
  27. * \brief <b>libopencm3 LM4F Universal Serial Bus controller </b>
  28. *
  29. * The LM4F USB driver is integrated with the libopencm3 USB stack. You should
  30. * use the generic stack.
  31. *
  32. * To use this driver, tell the linker to look for it:
  33. * @code{.c}
  34. * extern usbd_driver lm4f_usb_driver;
  35. * @endcode
  36. *
  37. * And pass this driver as an argument when initializing the USB stack:
  38. * @code{.c}
  39. * usbd_device *usbd_dev;
  40. * usbd_dev = usbd_init(&lm4f_usb_driver, ...);
  41. * @endcode
  42. *
  43. * <b>Polling or interrupt-driven? </b>
  44. *
  45. * The LM4F USB driver will work fine regardless of whether it is called from an
  46. * interrupt service routine, or from the main program loop.
  47. *
  48. * Polling USB from the main loop requires calling @ref usbd_poll() from the
  49. * main program loop.
  50. * For example:
  51. * @code{.c}
  52. * // Main program loop
  53. * while(1) {
  54. * usbd_poll(usb_dev);
  55. * do_other_stuff();
  56. * ...
  57. * @endcode
  58. *
  59. * Running @ref usbd_poll() from an interrupt has the advantage that it is only
  60. * called when needed, saving CPU cycles for the main program.
  61. *
  62. * RESET, DISCON, RESUME, and SUSPEND interrupts must be enabled, along with the
  63. * interrupts for any endpoint that is used. The EP0_TX interrupt must be
  64. * enabled for the control endpoint to function correctly.
  65. * For example, if EP1IN and EP2OUT are used, then the EP0_TX, EP1_TX, and
  66. * EP2_RX interrupts should be enabled:
  67. * @code{.c}
  68. * // Enable USB interrupts for EP0, EP1IN, and EP2OUT
  69. * ints = USB_INT_RESET | USB_INT_DISCON | USB_INT_RESUME |
  70. * USB_INT_SUSPEND;
  71. * usb_enable_interrupts(ints, USB_EP2_INT, USB_EP0_INT | USB_EP1_INT);
  72. * // Route the interrupts through the NVIC
  73. * nvic_enable_irq(NVIC_USB0_IRQ);
  74. * @endcode
  75. *
  76. * The USB ISR only has to call @ref usbd_poll().
  77. *
  78. * @code{.c}
  79. * void usb0_isr(void)
  80. * {
  81. * usbd_poll(usb_dev);
  82. * }
  83. * @endcode
  84. * @{
  85. */
  86. /*
  87. * TODO list:
  88. *
  89. * 1) Driver works by reading and writing to the FIFOs one byte at a time. It
  90. * has no knowledge of DMA.
  91. * 2) Double-buffering is supported. How can we take advantage of it to speed
  92. * up endpoint transfers.
  93. * 3) No benchmarks as to the endpoint's performance has been done.
  94. */
  95. /*
  96. * The following are resources referenced in comments:
  97. * [1] http://e2e.ti.com/support/microcontrollers/tiva_arm/f/908/t/238784.aspx
  98. */
  99. #include <libopencm3/cm3/common.h>
  100. #include <libopencm3/lm4f/usb.h>
  101. #include <libopencm3/lm4f/rcc.h>
  102. #include <libopencm3/usb/usbd.h>
  103. #include "../../lib/usb/usb_private.h"
  104. #include <stdbool.h>
  105. #define MAX_FIFO_RAM (4 * 1024)
  106. const struct _usbd_driver lm4f_usb_driver;
  107. /**
  108. * \brief Enable Specific USB Interrupts
  109. *
  110. * Enable any combination of interrupts. Interrupts may be OR'ed together to
  111. * enable them with one call. For example, to enable both the RESUME and RESET
  112. * interrupts, pass (USB_INT_RESUME | USB_INT_RESET)
  113. *
  114. * Note that the NVIC must be enabled and properly configured for the interrupt
  115. * to be routed to the CPU.
  116. *
  117. * @param[in] ints Interrupts which to enable. Any combination of interrupts may
  118. * be specified by OR'ing then together
  119. * @param[in] rx_ints Endpoints for which to generate an interrupt when a packet
  120. * packet is received.
  121. * @param[in] tx_ints Endpoints for which to generate an interrupt when a packet
  122. * packet is finished transmitting.
  123. */
  124. void usb_enable_interrupts(enum usb_interrupt ints,
  125. enum usb_ep_interrupt rx_ints,
  126. enum usb_ep_interrupt tx_ints)
  127. {
  128. USB_IE |= ints;
  129. USB_RXIE |= rx_ints;
  130. USB_TXIE |= tx_ints;
  131. }
  132. /**
  133. * \brief Disable Specific USB Interrupts
  134. *
  135. * Disable any combination of interrupts. Interrupts may be OR'ed together to
  136. * enable them with one call. For example, to disable both the RESUME and RESET
  137. * interrupts, pass (USB_INT_RESUME | USB_INT_RESET)
  138. *
  139. * Note that the NVIC must be enabled and properly configured for the interrupt
  140. * to be routed to the CPU.
  141. *
  142. * @param[in] ints Interrupts which to disable. Any combination of interrupts
  143. * may be specified by OR'ing then together
  144. * @param[in] rx_ints Endpoints for which to stop generating an interrupt when a
  145. * packet packet is received.
  146. * @param[in] tx_ints Endpoints for which to stop generating an interrupt when a
  147. * packet packet is finished transmitting.
  148. */
  149. void usb_disable_interrupts(enum usb_interrupt ints,
  150. enum usb_ep_interrupt rx_ints,
  151. enum usb_ep_interrupt tx_ints)
  152. {
  153. USB_IE &= ~ints;
  154. USB_RXIE &= ~rx_ints;
  155. USB_TXIE &= ~tx_ints;
  156. }
  157. /**
  158. * @cond private
  159. */
  160. static inline void lm4f_usb_soft_disconnect(void)
  161. {
  162. USB_POWER &= ~USB_POWER_SOFTCONN;
  163. }
  164. static inline void lm4f_usb_soft_connect(void)
  165. {
  166. USB_POWER |= USB_POWER_SOFTCONN;
  167. }
  168. static void lm4f_set_address(usbd_device *usbd_dev, uint8_t addr)
  169. {
  170. (void)usbd_dev;
  171. USB_FADDR = addr & USB_FADDR_FUNCADDR_MASK;
  172. }
  173. static void lm4f_ep_setup(usbd_device *usbd_dev, uint8_t addr, uint8_t type,
  174. uint16_t max_size,
  175. void (*callback) (usbd_device *usbd_dev, uint8_t ep))
  176. {
  177. (void)usbd_dev;
  178. (void)type;
  179. uint8_t reg8;
  180. uint16_t fifo_size;
  181. const bool dir_tx = addr & 0x80;
  182. const uint8_t ep = addr & 0x0f;
  183. /*
  184. * We do not mess with the maximum packet size, but we can only allocate
  185. * the FIFO in power-of-two increments.
  186. */
  187. if (max_size > 1024) {
  188. fifo_size = 2048;
  189. reg8 = USB_FIFOSZ_SIZE_2048;
  190. } else if (max_size > 512) {
  191. fifo_size = 1024;
  192. reg8 = USB_FIFOSZ_SIZE_1024;
  193. } else if (max_size > 256) {
  194. fifo_size = 512;
  195. reg8 = USB_FIFOSZ_SIZE_512;
  196. } else if (max_size > 128) {
  197. fifo_size = 256;
  198. reg8 = USB_FIFOSZ_SIZE_256;
  199. } else if (max_size > 64) {
  200. fifo_size = 128;
  201. reg8 = USB_FIFOSZ_SIZE_128;
  202. } else if (max_size > 32) {
  203. fifo_size = 64;
  204. reg8 = USB_FIFOSZ_SIZE_64;
  205. } else if (max_size > 16) {
  206. fifo_size = 32;
  207. reg8 = USB_FIFOSZ_SIZE_32;
  208. } else if (max_size > 8) {
  209. fifo_size = 16;
  210. reg8 = USB_FIFOSZ_SIZE_16;
  211. } else {
  212. fifo_size = 8;
  213. reg8 = USB_FIFOSZ_SIZE_8;
  214. }
  215. /* Endpoint 0 is more special */
  216. if (addr == 0) {
  217. USB_EPIDX = 0;
  218. if (reg8 > USB_FIFOSZ_SIZE_64) {
  219. reg8 = USB_FIFOSZ_SIZE_64;
  220. }
  221. /* The RX and TX FIFOs are shared for EP0 */
  222. USB_RXFIFOSZ = reg8;
  223. USB_TXFIFOSZ = reg8;
  224. /*
  225. * Regardless of how much we allocate, the first 64 bytes
  226. * are always reserved for EP0.
  227. */
  228. usbd_dev->fifo_mem_top_ep0 = 64;
  229. return;
  230. }
  231. /* Are we out of FIFO space? */
  232. if (usbd_dev->fifo_mem_top + fifo_size > MAX_FIFO_RAM) {
  233. return;
  234. }
  235. USB_EPIDX = addr & USB_EPIDX_MASK;
  236. /* FIXME: What about double buffering? */
  237. if (dir_tx) {
  238. USB_TXMAXP(ep) = max_size;
  239. USB_TXFIFOSZ = reg8;
  240. USB_TXFIFOADD = ((usbd_dev->fifo_mem_top) >> 3);
  241. if (callback) {
  242. usbd_dev->user_callback_ctr[ep][USB_TRANSACTION_IN] =
  243. (void *)callback;
  244. }
  245. if (type == USB_ENDPOINT_ATTR_ISOCHRONOUS) {
  246. USB_TXCSRH(ep) |= USB_TXCSRH_ISO;
  247. } else {
  248. USB_TXCSRH(ep) &= ~USB_TXCSRH_ISO;
  249. }
  250. } else {
  251. USB_RXMAXP(ep) = max_size;
  252. USB_RXFIFOSZ = reg8;
  253. USB_RXFIFOADD = ((usbd_dev->fifo_mem_top) >> 3);
  254. if (callback) {
  255. usbd_dev->user_callback_ctr[ep][USB_TRANSACTION_OUT] =
  256. (void *)callback;
  257. }
  258. if (type == USB_ENDPOINT_ATTR_ISOCHRONOUS) {
  259. USB_RXCSRH(ep) |= USB_RXCSRH_ISO;
  260. } else {
  261. USB_RXCSRH(ep) &= ~USB_RXCSRH_ISO;
  262. }
  263. }
  264. usbd_dev->fifo_mem_top += fifo_size;
  265. }
  266. static void lm4f_endpoints_reset(usbd_device *usbd_dev)
  267. {
  268. /*
  269. * The core resets the endpoints automatically on reset.
  270. * The first 64 bytes are always reserved for EP0
  271. */
  272. usbd_dev->fifo_mem_top = 64;
  273. }
  274. static void lm4f_ep_stall_set(usbd_device *usbd_dev, uint8_t addr,
  275. uint8_t stall)
  276. {
  277. (void)usbd_dev;
  278. const uint8_t ep = addr & 0x0f;
  279. const bool dir_tx = addr & 0x80;
  280. if (ep == 0) {
  281. if (stall) {
  282. USB_CSRL0 |= USB_CSRL0_STALL;
  283. } else {
  284. USB_CSRL0 &= ~USB_CSRL0_STALL;
  285. }
  286. return;
  287. }
  288. if (dir_tx) {
  289. if (stall) {
  290. (USB_TXCSRL(ep)) |= USB_TXCSRL_STALL;
  291. } else {
  292. (USB_TXCSRL(ep)) &= ~USB_TXCSRL_STALL;
  293. }
  294. } else {
  295. if (stall) {
  296. (USB_RXCSRL(ep)) |= USB_RXCSRL_STALL;
  297. } else {
  298. (USB_RXCSRL(ep)) &= ~USB_RXCSRL_STALL;
  299. }
  300. }
  301. }
  302. static uint8_t lm4f_ep_stall_get(usbd_device *usbd_dev, uint8_t addr)
  303. {
  304. (void)usbd_dev;
  305. const uint8_t ep = addr & 0x0f;
  306. const bool dir_tx = addr & 0x80;
  307. if (ep == 0) {
  308. return USB_CSRL0 & USB_CSRL0_STALLED;
  309. }
  310. if (dir_tx) {
  311. return USB_TXCSRL(ep) & USB_TXCSRL_STALLED;
  312. } else {
  313. return USB_RXCSRL(ep) & USB_RXCSRL_STALLED;
  314. }
  315. }
  316. static void lm4f_ep_nak_set(usbd_device *usbd_dev, uint8_t addr, uint8_t nak)
  317. {
  318. (void)usbd_dev;
  319. (void)addr;
  320. (void)nak;
  321. /* NAK's are handled automatically by hardware. Move along. */
  322. }
  323. static uint16_t lm4f_ep_write_packet(usbd_device *usbd_dev, uint8_t addr,
  324. const void *buf, uint16_t len)
  325. {
  326. const uint8_t ep = addr & 0xf;
  327. uint16_t i;
  328. (void)usbd_dev;
  329. /* Don't touch the FIFO if there is still a packet being transmitted */
  330. if (ep == 0 && (USB_CSRL0 & USB_CSRL0_TXRDY)) {
  331. return 0;
  332. } else if (USB_TXCSRL(ep) & USB_TXCSRL_TXRDY) {
  333. return 0;
  334. }
  335. /*
  336. * We don't need to worry about buf not being aligned. If it's not,
  337. * the reads are downgraded to 8-bit in hardware. We lose a bit of
  338. * performance, but we don't crash.
  339. */
  340. for (i = 0; i < (len & ~0x3); i += 4) {
  341. USB_FIFO32(ep) = *((uint32_t *)(buf + i));
  342. }
  343. if (len & 0x2) {
  344. USB_FIFO16(ep) = *((uint16_t *)(buf + i));
  345. i += 2;
  346. }
  347. if (len & 0x1) {
  348. USB_FIFO8(ep) = *((uint8_t *)(buf + i));
  349. i += 1;
  350. }
  351. if (ep == 0) {
  352. /*
  353. * EP0 is very special. We should only set DATAEND when we
  354. * transmit the last packet in the transaction. A transaction
  355. * that is a multiple of 64 bytes will end with a zero-length
  356. * packet, so our check is sane.
  357. */
  358. if (len != 64) {
  359. USB_CSRL0 |= USB_CSRL0_TXRDY | USB_CSRL0_DATAEND;
  360. } else {
  361. USB_CSRL0 |= USB_CSRL0_TXRDY;
  362. }
  363. } else {
  364. USB_TXCSRL(ep) |= USB_TXCSRL_TXRDY;
  365. }
  366. return i;
  367. }
  368. static uint16_t lm4f_ep_read_packet(usbd_device *usbd_dev, uint8_t addr,
  369. void *buf, uint16_t len)
  370. {
  371. (void)usbd_dev;
  372. uint16_t rlen;
  373. uint8_t ep = addr & 0xf;
  374. uint16_t fifoin = USB_RXCOUNT(ep);
  375. rlen = (fifoin > len) ? len : fifoin;
  376. /*
  377. * We don't need to worry about buf not being aligned. If it's not,
  378. * the writes are downgraded to 8-bit in hardware. We lose a bit of
  379. * performance, but we don't crash.
  380. */
  381. for (len = 0; len < (rlen & ~0x3); len += 4) {
  382. *((uint32_t *)(buf + len)) = USB_FIFO32(ep);
  383. }
  384. if (rlen & 0x2) {
  385. *((uint16_t *)(buf + len)) = USB_FIFO16(ep);
  386. len += 2;
  387. }
  388. if (rlen & 0x1) {
  389. *((uint8_t *)(buf + len)) = USB_FIFO8(ep);
  390. }
  391. if (ep == 0) {
  392. /*
  393. * Clear RXRDY
  394. * Datasheet says that DATAEND must also be set when clearing
  395. * RXRDY. We don't do that. If did this when transmitting a
  396. * packet larger than 64 bytes, only the first 64 bytes would
  397. * be transmitted, followed by a handshake. The host would only
  398. * get 64 bytes, seeing it as a malformed packet. Usually, we
  399. * would not get past enumeration.
  400. */
  401. USB_CSRL0 |= USB_CSRL0_RXRDYC;
  402. } else {
  403. USB_RXCSRL(ep) &= ~USB_RXCSRL_RXRDY;
  404. }
  405. return rlen;
  406. }
  407. static void lm4f_poll(usbd_device *usbd_dev)
  408. {
  409. void (*tx_cb)(usbd_device *usbd_dev, uint8_t ea);
  410. void (*rx_cb)(usbd_device *usbd_dev, uint8_t ea);
  411. int i;
  412. /*
  413. * The initial state of these registers might change, as we process the
  414. * interrupt, but we need the initial state in order to decide how to
  415. * handle events.
  416. */
  417. const uint8_t usb_is = USB_IS;
  418. const uint8_t usb_rxis = USB_RXIS;
  419. const uint8_t usb_txis = USB_TXIS;
  420. const uint8_t usb_csrl0 = USB_CSRL0;
  421. if ((usb_is & USB_IM_SUSPEND) && (usbd_dev->user_callback_suspend)) {
  422. usbd_dev->user_callback_suspend();
  423. }
  424. if ((usb_is & USB_IM_RESUME) && (usbd_dev->user_callback_resume)) {
  425. usbd_dev->user_callback_resume();
  426. }
  427. if (usb_is & USB_IM_RESET) {
  428. _usbd_reset(usbd_dev);
  429. }
  430. if ((usb_is & USB_IM_SOF) && (usbd_dev->user_callback_sof)) {
  431. usbd_dev->user_callback_sof();
  432. }
  433. if (usb_txis & USB_EP0) {
  434. /*
  435. * The EP0 bit in USB_TXIS is special. It tells us that
  436. * something happened on EP0, but does not tell us what. This
  437. * bit does not necessarily tell us that a packet was
  438. * transmitted, so we have to go through all the possibilities
  439. * to figure out exactly what did. Only after we've exhausted
  440. * all other possibilities, can we assume this is a EPO
  441. * "transmit complete" interrupt.
  442. */
  443. if (usb_csrl0 & USB_CSRL0_RXRDY) {
  444. enum _usbd_transaction type;
  445. type = (usbd_dev->control_state.state != DATA_OUT &&
  446. usbd_dev->control_state.state != LAST_DATA_OUT)
  447. ? USB_TRANSACTION_SETUP :
  448. USB_TRANSACTION_OUT;
  449. if (type == USB_TRANSACTION_SETUP) {
  450. lm4f_ep_read_packet(usbd_dev, 0, &usbd_dev->control_state.req, 8);
  451. }
  452. if (usbd_dev->user_callback_ctr[0][type]) {
  453. usbd_dev->
  454. user_callback_ctr[0][type](usbd_dev, 0);
  455. }
  456. } else {
  457. tx_cb = usbd_dev->user_callback_ctr[0]
  458. [USB_TRANSACTION_IN];
  459. /*
  460. * EP0 bit in TXIS is set not only when a packet is
  461. * finished transmitting, but also when RXRDY is set, or
  462. * when we set TXRDY to transmit a packet. If any of
  463. * those are the case, then we do not want to call our
  464. * IN callback, since the state machine will be in the
  465. * wrong state, and we'll just stall our control
  466. * endpoint.
  467. * In fact, the only way to know if it's time to call
  468. * our TX callback is to know what to expect. The
  469. * hardware does not tell us what sort of transaction
  470. * this is. We need to work with the state machine to
  471. * figure it all out. See [1] for details.
  472. */
  473. if ((usbd_dev->control_state.state != DATA_IN) &&
  474. (usbd_dev->control_state.state != LAST_DATA_IN) &&
  475. (usbd_dev->control_state.state != STATUS_IN)) {
  476. return;
  477. }
  478. if (tx_cb) {
  479. tx_cb(usbd_dev, 0);
  480. }
  481. }
  482. }
  483. /* See which interrupt occurred */
  484. for (i = 1; i < 8; i++) {
  485. tx_cb = usbd_dev->user_callback_ctr[i][USB_TRANSACTION_IN];
  486. rx_cb = usbd_dev->user_callback_ctr[i][USB_TRANSACTION_OUT];
  487. if ((usb_txis & (1 << i)) && tx_cb) {
  488. tx_cb(usbd_dev, i);
  489. }
  490. if ((usb_rxis & (1 << i)) && rx_cb) {
  491. rx_cb(usbd_dev, i);
  492. }
  493. }
  494. }
  495. static void lm4f_disconnect(usbd_device *usbd_dev, bool disconnected)
  496. {
  497. (void)usbd_dev;
  498. /*
  499. * This is all it takes:
  500. * usbd_disconnect(dev, 1) followed by usbd_disconnect(dev, 0)
  501. * causes the device to re-enumerate and re-configure properly.
  502. */
  503. if (disconnected) {
  504. lm4f_usb_soft_disconnect();
  505. } else {
  506. lm4f_usb_soft_connect();
  507. }
  508. }
  509. /*
  510. * A static struct works as long as we have only one USB peripheral. If we
  511. * meet LM4Fs with more than one USB, then we need to rework this approach.
  512. */
  513. static struct _usbd_device usbd_dev;
  514. /** Initialize the USB device controller hardware of the LM4F. */
  515. static usbd_device *lm4f_usbd_init(void)
  516. {
  517. int i;
  518. /* Start the USB clock */
  519. periph_clock_enable(RCC_USB0);
  520. /* Enable the USB PLL interrupts - used to assert PLL lock */
  521. SYSCTL_IMC |= (SYSCTL_IMC_USBPLLLIM | SYSCTL_IMC_PLLLIM);
  522. rcc_usb_pll_on();
  523. /* Make sure we're disconnected. We'll reconnect later */
  524. lm4f_usb_soft_disconnect();
  525. /* Software reset USB */
  526. SYSCTL_SRUSB = 1;
  527. for (i = 0; i < 1000; i++) {
  528. __asm__("nop");
  529. }
  530. SYSCTL_SRUSB = 0;
  531. /*
  532. * Wait for the PLL to lock before soft connecting
  533. * This will result in a deadlock if the system clock is not setup
  534. * correctly (clock from main oscillator).
  535. */
  536. /* Wait for it */
  537. i = 0;
  538. while ((SYSCTL_RIS & SYSCTL_RIS_USBPLLLRIS) == 0) {
  539. i++;
  540. if (i > 0xffff) {
  541. return 0;
  542. }
  543. }
  544. /* Now connect to USB */
  545. lm4f_usb_soft_connect();
  546. /* No FIFO allocated yet, but the first 64 bytes are still reserved */
  547. usbd_dev.fifo_mem_top = 64;
  548. return &usbd_dev;
  549. }
  550. /* What is this thing even good for */
  551. #define RX_FIFO_SIZE 512
  552. const struct _usbd_driver lm4f_usb_driver = {
  553. .init = lm4f_usbd_init,
  554. .set_address = lm4f_set_address,
  555. .ep_setup = lm4f_ep_setup,
  556. .ep_reset = lm4f_endpoints_reset,
  557. .ep_stall_set = lm4f_ep_stall_set,
  558. .ep_stall_get = lm4f_ep_stall_get,
  559. .ep_nak_set = lm4f_ep_nak_set,
  560. .ep_write_packet = lm4f_ep_write_packet,
  561. .ep_read_packet = lm4f_ep_read_packet,
  562. .poll = lm4f_poll,
  563. .disconnect = lm4f_disconnect,
  564. .base_address = USB_BASE,
  565. .set_address_before_status = false,
  566. .rx_fifo_size = RX_FIFO_SIZE,
  567. };
  568. /**
  569. * @endcond
  570. */
  571. /**
  572. * @}
  573. */