usart_cb.c 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433
  1. /*
  2. * Copyright (C) 2012 by egnite GmbH
  3. * Copyright (C) 2001-2003 by egnite Software GmbH
  4. *
  5. * All rights reserved.
  6. *
  7. * Redistribution and use in source and binary forms, with or without
  8. * modification, are permitted provided that the following conditions
  9. * are met:
  10. *
  11. * 1. Redistributions of source code must retain the above copyright
  12. * notice, this list of conditions and the following disclaimer.
  13. * 2. Redistributions in binary form must reproduce the above copyright
  14. * notice, this list of conditions and the following disclaimer in the
  15. * documentation and/or other materials provided with the distribution.
  16. * 3. Neither the name of the copyright holders nor the names of
  17. * contributors may be used to endorse or promote products derived
  18. * from this software without specific prior written permission.
  19. *
  20. * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
  21. * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
  22. * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
  23. * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
  24. * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
  25. * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
  26. * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
  27. * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
  28. * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
  29. * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
  30. * THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
  31. * SUCH DAMAGE.
  32. *
  33. * For additional information see http://www.ethernut.de/
  34. */
  35. /*!
  36. * \file dev/usart_cb.c
  37. * \brief Hardware independent part of a circular buffer based U(S)ART
  38. * driver.
  39. *
  40. * \verbatim
  41. * $Id$
  42. * \endverbatim
  43. */
  44. #include <dev/usart.h>
  45. #include <sys/nutdebug.h>
  46. #include <stdlib.h>
  47. #include <string.h>
  48. #include <fcntl.h>
  49. #include <dev/usart_cb.h>
  50. int UsartCbInit(NUTDEVICE *dev)
  51. {
  52. USARTCB_DCB *dcb;
  53. /* Do sanity checks if NUTDEBUG_USE_ASSERT is defined. */
  54. NUTASSERT(dev != NULL);
  55. NUTASSERT(dev->dev_dcb != NULL);
  56. dcb = (USARTCB_DCB *) dev->dev_dcb;
  57. return dcb->usart_enable(dcb);
  58. }
  59. NUTFILE *UsartCbOpen(NUTDEVICE *dev, const char *name, int mode, int acc)
  60. {
  61. int rc = -1;
  62. NUTFILE *nfp;
  63. USARTCB_DCB *dcb;
  64. /* Do sanity checks if NUTDEBUG_USE_ASSERT is defined. */
  65. NUTASSERT(dev != NULL);
  66. NUTASSERT(dev->dev_dcb != NULL);
  67. dcb = (USARTCB_DCB *) dev->dev_dcb;
  68. nfp = calloc(1, sizeof(*nfp));
  69. if (nfp) {
  70. nfp->nf_dev = dev;
  71. /* We assume, that UART interrupts are inactive. */
  72. rc = CircBuffReset((CIRCBUFF *) &dcb->usart_rx_buff, 255);
  73. if (rc == 0) {
  74. rc = CircBuffReset((CIRCBUFF *) &dcb->usart_tx_buff, 255);
  75. }
  76. }
  77. /* Release resources on any error. */
  78. if (rc) {
  79. free (nfp);
  80. CircBuffReset((CIRCBUFF *) &dcb->usart_tx_buff, 0);
  81. CircBuffReset((CIRCBUFF *) &dcb->usart_rx_buff, 0);
  82. return NUTFILE_EOF;
  83. }
  84. /* Set proper device modes. */
  85. if ((mode & 0xC000) == _O_BINARY) {
  86. dcb->usart_mode &= ~USART_MF_COOKEDMODE;
  87. } else {
  88. dcb->usart_mode |= USART_MF_COOKEDMODE;
  89. }
  90. /* Set default watermarks to 1/4 and 3/4. */
  91. dcb->usart_rx_lowm = dcb->usart_rx_buff.rxb_siz / 4;
  92. dcb->usart_rx_hiwm = dcb->usart_rx_lowm * 3;
  93. dcb->usart_rx_start(dcb);
  94. return nfp;
  95. }
  96. int UsartCbClose(NUTFILE *nfp)
  97. {
  98. int rc = 0;
  99. USARTCB_DCB *dcb;
  100. USARTCB_TXBUFF *txcb;
  101. /* Optional sanity checks if NUTDEBUG_USE_ASSERT is defined. */
  102. NUTASSERT(nfp != NULL && nfp != NUTFILE_EOF);
  103. NUTASSERT(nfp->nf_dev != NULL);
  104. NUTASSERT(nfp->nf_dev->dev_dcb != NULL);
  105. dcb = (USARTCB_DCB *) nfp->nf_dev->dev_dcb;
  106. txcb = &dcb->usart_tx_buff;
  107. /* Flush the complete output buffer. If this times out, let the
  108. caller know, that not all bytes had been transmitted. */
  109. while (txcb->txb_wri != txcb->txb_rdi) {
  110. dcb->usart_tx_start(dcb);
  111. if (NutEventWait(&txcb->txb_que, dcb->usart_wr_tmo)) {
  112. rc = -1;
  113. }
  114. }
  115. /* Disable interrupts and release buffers. */
  116. dcb->usart_rx_stop(dcb);
  117. CircBuffReset((CIRCBUFF *) &dcb->usart_rx_buff, 0);
  118. dcb->usart_tx_stop(dcb);
  119. CircBuffReset((CIRCBUFF *) &dcb->usart_tx_buff, 0);
  120. return rc;
  121. }
  122. int UsartCbRead(NUTFILE *nfp, void *buffer, int size)
  123. {
  124. int rc;
  125. USARTCB_DCB *dcb;
  126. USARTCB_RXBUFF *rxcb;
  127. uint8_t *cp = (uint8_t *) buffer;
  128. /* Optional sanity checks if NUTDEBUG_USE_ASSERT is defined. */
  129. NUTASSERT(nfp != NULL && nfp != NUTFILE_EOF);
  130. NUTASSERT(nfp->nf_dev != NULL);
  131. NUTASSERT(nfp->nf_dev->dev_dcb != NULL);
  132. NUTASSERT(buffer || (buffer == NULL && size == 0));
  133. dcb = (USARTCB_DCB *) nfp->nf_dev->dev_dcb;
  134. rxcb = &dcb->usart_rx_buff;
  135. /* Call without data pointer discards the receive buffer. */
  136. if (buffer == NULL) {
  137. dcb->usart_rx_stop(dcb);
  138. rxcb->rxb_rdi = rxcb->rxb_wri;
  139. rxcb->rxb_cnt = 0;
  140. dcb->usart_rx_start(dcb);
  141. return 0;
  142. }
  143. /* Use a byte pointer to access the caller's buffer. */
  144. cp = (uint8_t *) buffer;
  145. /* Loop until the requested number of bytes had been copied to the
  146. caller's buffer. Note, the internal premature return in case
  147. of an empty buffer. */
  148. for (rc = 0; rc < (size_t) size;) {
  149. size_t chunk;
  150. /* Retrieve the number of consecutive bytes available in the
  151. circular buffer. Note, that we must disable the receiver
  152. to get exclusive access to the buffer. */
  153. dcb->usart_rx_stop(dcb);
  154. chunk = CircBuffReadSize((CIRCBUFF *) rxcb);
  155. dcb->usart_rx_start(dcb);
  156. if (chunk == 0) {
  157. /* If the receive buffer is empty, then wait until at least
  158. one character had been available or until a read timeout
  159. occurs. */
  160. if (rc || NutEventWait(&rxcb->rxb_que, dcb->usart_rd_tmo)) {
  161. /* Return the number of bytes we got so far. */
  162. return rc;
  163. }
  164. } else {
  165. /* Limit the read size to the caller's buffer size. */
  166. chunk = chunk > (size - rc) ? (size - rc) : chunk;
  167. /* In cooked mode we will translate carriage return linefeed
  168. sequences as well as single carriage returns to linefeeds. */
  169. if (dcb->usart_mode & USART_MF_COOKEDMODE) {
  170. int i;
  171. uint_fast8_t ch;
  172. for (i = 0; i < chunk; i++) {
  173. ch = rxcb->rxb_buf[rxcb->rxb_rdi + i];
  174. if (ch == '\r') {
  175. ch = '\n';
  176. dcb->usart_rx_cr = 1;
  177. }
  178. else if (dcb->usart_rx_cr) {
  179. if (ch == '\n') {
  180. ch = 0;
  181. }
  182. dcb->usart_rx_cr = 0;
  183. }
  184. if (ch) {
  185. *cp++ = ch;
  186. rc++;
  187. }
  188. }
  189. } else {
  190. /* In binary mode we can copy the complete chunk in one go. */
  191. memcpy(cp, (void *)&rxcb->rxb_buf[rxcb->rxb_rdi], chunk);
  192. cp += chunk;
  193. rc += chunk;
  194. }
  195. /* Update the circular buffer after we removed some data.
  196. Again, that we must disable the receiver to get exclusive
  197. access. Note, that this will automatically re-enable the
  198. receiver in case it has been stopped on a buffer overflow. */
  199. dcb->usart_rx_stop(dcb);
  200. rxcb->rxb_rdi = (rxcb->rxb_rdi + chunk) & rxcb->rxb_siz;
  201. rxcb->rxb_cnt -= chunk;
  202. dcb->usart_rx_start(dcb);
  203. }
  204. }
  205. return rc;
  206. }
  207. int UsartCbWrite(NUTFILE *nfp, const void *buffer, int len)
  208. {
  209. int rc;
  210. USARTCB_DCB *dcb;
  211. USARTCB_TXBUFF *txcb;
  212. size_t chunk;
  213. uint8_t *cp = (uint8_t *) buffer;
  214. uint_fast8_t eol = 0;
  215. /* Optional sanity checks if NUTDEBUG_USE_ASSERT is defined. */
  216. NUTASSERT(nfp != NULL && nfp != NUTFILE_EOF);
  217. NUTASSERT(nfp->nf_dev != NULL);
  218. NUTASSERT(nfp->nf_dev->dev_dcb != NULL);
  219. NUTASSERT(buffer || (buffer == NULL && size == 0));
  220. dcb = (USARTCB_DCB *) nfp->nf_dev->dev_dcb;
  221. txcb = &dcb->usart_tx_buff;
  222. /* Call without data pointer flushes the buffer. In this case a
  223. value not equal zero indicates write timeout. */
  224. if (buffer == NULL) {
  225. while (txcb->txb_wri != txcb->txb_rdi) {
  226. dcb->usart_tx_start(dcb);
  227. if (NutEventWait(&txcb->txb_que, dcb->usart_wr_tmo)) {
  228. return -1;
  229. }
  230. }
  231. return 0;
  232. }
  233. for (rc = 0; rc < len || eol; ) {
  234. /* Disable the low level transmitter. This is required to get
  235. exclusive access to the transmit buffer. */
  236. dcb->usart_tx_stop(dcb);
  237. chunk = CircBuffWriteSize((CIRCBUFF *) txcb);
  238. if (chunk == 0) {
  239. dcb->usart_tx_start(dcb);
  240. if (NutEventWait(&txcb->txb_que, dcb->usart_wr_tmo)) {
  241. /* Timeout. */
  242. return rc;
  243. }
  244. continue;
  245. }
  246. if (eol) {
  247. txcb->txb_buf[txcb->txb_wri++] = '\n';
  248. eol = 0;
  249. chunk--;
  250. }
  251. chunk = chunk > (len - rc) ? (len - rc) : chunk;
  252. if (dcb->usart_mode & USART_MF_COOKEDMODE) {
  253. int i;
  254. for (i = 0; i < chunk; i++) {
  255. eol = *cp == '\n';
  256. if (eol) {
  257. txcb->txb_buf[txcb->txb_wri + i] = '\r';
  258. cp++;
  259. chunk = i + 1;
  260. break;
  261. }
  262. txcb->txb_buf[txcb->txb_wri + i] = *cp++;
  263. }
  264. } else {
  265. memcpy(&txcb->txb_buf[txcb->txb_wri], cp, chunk);
  266. cp += chunk;
  267. }
  268. txcb->txb_wri = (txcb->txb_wri + chunk) & txcb->txb_siz;
  269. txcb->txb_cnt += chunk;
  270. /* Re-enable the low level transmitter. */
  271. dcb->usart_tx_start(dcb);
  272. rc += chunk;
  273. }
  274. return rc;
  275. }
  276. #ifdef __HARVARD_ARCH__
  277. int UsartCbWrite_P(NUTFILE *nfp, PGM_P buffer, int len)
  278. {
  279. return -1;
  280. }
  281. #endif
  282. int UsartCbIoCtrl(NUTDEVICE *dev, int req, void *conf)
  283. {
  284. int rc = 0;
  285. USARTCB_DCB *dcb;
  286. uint32_t *u32vp = (uint32_t *) conf;
  287. /* Debug sanity check. */
  288. NUTASSERT(dev != NULL);
  289. dcb = (USARTCB_DCB *) dev->dev_dcb;
  290. switch (req) {
  291. case UART_SETREADTIMEOUT:
  292. NUTASSERT(dev->dev_dcb != NULL);
  293. dcb->usart_rd_tmo = *u32vp;
  294. break;
  295. case UART_GETREADTIMEOUT:
  296. NUTASSERT(dev->dev_dcb != NULL);
  297. *u32vp = dcb->usart_rd_tmo;
  298. break;
  299. case UART_SETWRITETIMEOUT:
  300. NUTASSERT(dev->dev_dcb != NULL);
  301. dcb->usart_wr_tmo = *u32vp;
  302. break;
  303. case UART_GETWRITETIMEOUT:
  304. NUTASSERT(dev->dev_dcb != NULL);
  305. *u32vp = dcb->usart_wr_tmo;
  306. break;
  307. case UART_SETCOOKEDMODE:
  308. if (*u32vp) {
  309. dcb->usart_mode |= USART_MF_COOKEDMODE;
  310. } else {
  311. dcb->usart_mode &= ~USART_MF_COOKEDMODE;
  312. }
  313. break;
  314. case UART_GETCOOKEDMODE:
  315. if (dcb->usart_mode & USART_MF_COOKEDMODE) {
  316. *u32vp = 1;
  317. } else {
  318. *u32vp = 0;
  319. }
  320. break;
  321. case UART_SETTXBUFSIZ:
  322. dcb->usart_tx_stop(dcb);
  323. rc = CircBuffReset((CIRCBUFF *) &dcb->usart_tx_buff, *u32vp);
  324. if (rc == 0) {
  325. dcb->usart_tx_start(dcb);
  326. }
  327. break;
  328. case UART_GETTXBUFSIZ:
  329. *u32vp = dcb->usart_tx_buff.txb_siz;
  330. break;
  331. case UART_SETRXBUFSIZ:
  332. dcb->usart_rx_stop(dcb);
  333. rc = CircBuffReset((CIRCBUFF *) &dcb->usart_rx_buff, *u32vp);
  334. if (rc == 0) {
  335. dcb->usart_rx_start(dcb);
  336. }
  337. break;
  338. case UART_GETRXBUFSIZ:
  339. *u32vp = dcb->usart_rx_buff.rxb_siz;
  340. break;
  341. case UART_SETRXBUFLWMARK:
  342. dcb->usart_rx_stop(dcb);
  343. dcb->usart_rx_lowm = *u32vp;
  344. dcb->usart_rx_start(dcb);
  345. break;
  346. case UART_GETRXBUFLWMARK:
  347. *u32vp = dcb->usart_rx_lowm;
  348. break;
  349. case UART_SETRXBUFHWMARK:
  350. dcb->usart_rx_stop(dcb);
  351. dcb->usart_rx_hiwm = *u32vp;
  352. dcb->usart_rx_start(dcb);
  353. break;
  354. case UART_GETRXBUFHWMARK:
  355. *u32vp = dcb->usart_rx_hiwm;
  356. break;
  357. default:
  358. NUTASSERT(dcb->usart_control != NULL);
  359. rc = dcb->usart_control(dcb, req, conf);
  360. break;
  361. }
  362. return rc;
  363. }
  364. long UsartCbSize(NUTFILE *nfp)
  365. {
  366. long rc;
  367. USARTCB_DCB *dcb;
  368. USARTCB_RXBUFF *rxcb;
  369. NUTASSERT(nfp != NULL && nfp != NUTFILE_EOF);
  370. NUTASSERT(nfp->nf_dev != NULL);
  371. NUTASSERT(nfp->nf_dev->dev_dcb != NULL);
  372. dcb = (USARTCB_DCB *) nfp->nf_dev->dev_dcb;
  373. rxcb = &dcb->usart_rx_buff;
  374. /* Retrieve the number of bytes currently available in the
  375. circular buffer. Note, that we must disable the receiver
  376. to get exclusive access to the buffer. */
  377. dcb->usart_rx_stop(dcb);
  378. rc = rxcb->rxb_wri - rxcb->rxb_rdi;
  379. if (rxcb->rxb_rdi > rxcb->rxb_wri) {
  380. rc += rxcb->rxb_siz + 1;
  381. }
  382. dcb->usart_rx_start(dcb);
  383. return rc;
  384. }