select.c 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464
  1. /*
  2. * Copyright (C) 2013 Ole Reinhardt <ole.reinhardt@embedded-it.de>
  3. *
  4. * Redistribution and use in source and binary forms, with or without
  5. * modification, are permitted provided that the following conditions
  6. * are met:
  7. *
  8. * 1. Redistributions of source code must retain the above copyright
  9. * notice, this list of conditions and the following disclaimer.
  10. * 2. Redistributions in binary form must reproduce the above copyright
  11. * notice, this list of conditions and the following disclaimer in the
  12. * documentation and/or other materials provided with the distribution.
  13. * 3. Neither the name of the copyright holders nor the names of
  14. * contributors may be used to endorse or promote products derived
  15. * from this software without specific prior written permission.
  16. *
  17. * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
  18. * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
  19. * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
  20. * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
  21. * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
  22. * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
  23. * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
  24. * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
  25. * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
  26. * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
  27. * THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
  28. * SUCH DAMAGE.
  29. *
  30. * For additional information see http://www.ethernut.de/
  31. *
  32. */
  33. /*!
  34. * \verbatim
  35. * $Id: $
  36. * \endverbatim
  37. */
  38. #include "cfg/crt.h" /* Must be included first! */
  39. #ifndef CRT_DISABLE_SELECT_POLL
  40. #include "nut_io.h"
  41. #include <stdlib.h>
  42. #include <string.h>
  43. #include <errno.h>
  44. #include <sys/time.h>
  45. #include <sys/types.h>
  46. #include <sys/device.h>
  47. #include <sys/nutdebug.h>
  48. #include <sys/atom.h>
  49. #include <sys/event.h>
  50. #include <sys/select.h>
  51. /*!
  52. * \addtogroup xgCrtLowio
  53. */
  54. /*@{*/
  55. #define MAX_TIMEOUT_SEC ((~0UL) / 1000) - 1
  56. /*!
  57. * \brief Wake up selects() waiting in the given wait queue list.
  58. *
  59. * NutSelectWakeup() is used to wake up the waiting selects() as soon as the
  60. * file state / driver state has changed.
  61. *
  62. * May not be called from interrupt context!!!
  63. *
  64. * \param wq_list Pointer to the linked list of event queues
  65. *
  66. * \param flags Flag set represending the events that are currently
  67. * outstandings. These flags will be matched to the flags of
  68. * the waiting select calls to only wake up selects that are
  69. * realy interested in the current event.
  70. */
  71. void NutSelectWakeup(WQLIST *wq_list, uint_fast8_t flags)
  72. {
  73. while (wq_list) {
  74. if (flags & wq_list->flags) {
  75. NutEventPostAsync(wq_list->wq);
  76. }
  77. wq_list = wq_list->next;
  78. }
  79. }
  80. /*!
  81. * \brief Wake up selects() waiting in the given wait queue list from interrupt
  82. * context.
  83. *
  84. * NutSelectWakeupFromIrq() is the same as NutSelectWakeup() but shall called
  85. * from interrupt context.
  86. *
  87. * \param wq_list Pointer to the linked list of event queues
  88. *
  89. * \param flags Flag set represending the events that are currently
  90. * outstandings. These flags will be matched to the flags of
  91. * the waiting select calls to only wake up selects that are
  92. * realy interested in the current event.
  93. */
  94. void NutSelectWakeupFromIrq(WQLIST *wq_list, uint_fast8_t flags)
  95. {
  96. while (wq_list) {
  97. if (flags & wq_list->flags) {
  98. NutEventPostFromIrq(wq_list->wq);
  99. }
  100. wq_list = wq_list->next;
  101. }
  102. }
  103. /*!
  104. * \brief Helper function to manage wait queue lists
  105. *
  106. * NutSelectManageWq() must be called from the drivers dev_slect fop function
  107. * to manage the driver wait queue list.
  108. *
  109. * \param wq_list Pointer to the linked list of event queues, currently added
  110. * to the drivers wait queue
  111. * \param wq Event queue handle to be added or removed
  112. * \param flags Flag set represending the different type of monitoring
  113. * requests for the currently managed file descriptor
  114. * \param cmd select control command:
  115. * - SELECT_CMD_NOP: Do nothing
  116. * - SELECT_CMD_INIT: Add the event queue to the drivers
  117. * wait queue list
  118. * - SELECT_CMD_CLEANUP: Remove the event queue from the
  119. * drivers wait queue list
  120. *
  121. * \return Returns the current event state of the selected file
  122. */
  123. void NutSelectManageWq(WQLIST **wq_list, HANDLE *wq, int flags, select_cmd_t cmd)
  124. {
  125. WQLIST *wl_entry;
  126. WQLIST **wl_tmp;
  127. if ((cmd != SELECT_CMD_NOP) && (wq_list != NULL) && (wq != NULL) && (flags != 0)) {
  128. switch (cmd) {
  129. case SELECT_CMD_INIT:
  130. /* Add our waitqueue to the device waitqueue list */
  131. /* Allocate a new waitqueue list entry and put us on the wait
  132. list of the current NUTFILE
  133. */
  134. wl_entry = malloc(sizeof(WQLIST));
  135. wl_entry->wq = wq;
  136. wl_entry->flags = flags;
  137. /* Modifications of the wait list should be done atomic */
  138. NutEnterCritical();
  139. wl_entry->next = *wq_list;
  140. *wq_list = wl_entry;
  141. NutExitCritical();
  142. break;
  143. case SELECT_CMD_CLEANUP:
  144. /* Cleanup the waitqueu list of the device, remove our entry which is matched by it's address */
  145. wl_entry = NULL;
  146. /* Wait queue list processing should be done atomic */
  147. NutEnterCritical();
  148. wl_tmp = wq_list;
  149. /* Iterate through the waitqueue list */
  150. while (*wl_tmp) {
  151. /* Check if we found 'our' entry */
  152. if ((*wl_tmp)->wq == wq) {
  153. /* Remove the list entry and stop searching */
  154. wl_entry = *wl_tmp;
  155. *wl_tmp = (*wl_tmp)->next;
  156. break;
  157. } else {
  158. /* Move forward to the next entry */
  159. *wl_tmp = (*wl_tmp)->next;
  160. }
  161. }
  162. NutExitCritical();
  163. /* Free the allocated memeory of the list entry */
  164. if (wl_entry) {
  165. free(wl_entry);
  166. }
  167. break;
  168. default:
  169. break;
  170. }
  171. }
  172. }
  173. /*!
  174. * \brief Loop through all file descriptors and call the drivers dev_select fop
  175. *
  176. * select_scan() loops through all file descriptos of the fd sets passed to
  177. * select(). For each fd that is represented by one ore more of the three sets
  178. * the drivers dev_select fop is called and the return value is added to the
  179. * corresponding rflags array entry.
  180. *
  181. * \param n the highest-numbered file descriptor in any of the three
  182. * sets plus 1
  183. * \param flags Flag set array which represents the different type of
  184. * monitoring requests for this file descriptor (read / write /
  185. * exception)
  186. * \param rflags Flag set which will be filled with the events that had been
  187. * recorded in the meantime
  188. * \param wq Event queue handle that shall be passed to the driver. This
  189. * event queue will be added to the drivers wait queue list on
  190. * the first call of select_scan and is later removed again.
  191. * \param cmd select control command:
  192. * - SELECT_CMD_NOP: Just scan for events
  193. * - SELECT_CMD_INIT: Scan for events and add our event
  194. * queue to the drivers wait queue list
  195. * - SELECT_CMD_CLEANUP: Scan for events and remove our event
  196. * queue from the drivers wait queue list
  197. *
  198. * \return Returns the current event state of the selected file
  199. */
  200. static int select_scan(int n, uint8_t *flags, uint8_t *rflags, HANDLE *wq, select_cmd_t cmd)
  201. {
  202. NUTFILE *fp;
  203. NUTDEVICE *dev;
  204. int fd;
  205. uint_fast8_t result;
  206. int count;
  207. count = 0;
  208. /* Loop through all file descriptors */
  209. for (fd = 0; fd < n; fd ++) {
  210. /* If the fd has set one of the flags... */
  211. if (flags[fd]) {
  212. fp = __fds[fd];
  213. dev = fp->nf_dev;
  214. /* Call the select function of the driver, to check the blocking modes and error state */
  215. if (((dev != NULL) && (dev->dev_select != NULL) && (result = dev->dev_select(fp, flags[fd], wq, cmd))) ||
  216. ((dev == NULL) && (((NUTVIRTUALDEVICE *) fp)->vdv_select != NULL) && (result = ((NUTVIRTUALDEVICE *) fp)->vdv_select(fp, flags[fd], wq, cmd)))) {
  217. /* Update the number of changed fds */
  218. count ++;
  219. /* only include the requested flags in the result */
  220. result &= flags[fd];
  221. /* Write back the result */
  222. rflags[fd] |= result;
  223. }
  224. }
  225. }
  226. /* Return number of filedescriptors that have set an event */
  227. return count;
  228. }
  229. /*!
  230. * \brief Loop through all file descriptors and poll their state.
  231. *
  232. * do_select() loops up to three times through the file descriptors of the
  233. * fd sets passed to select().
  234. * The first loop adds an event queue to the wait queue of each file descriptor
  235. * and also checks if any events are just pending.
  236. *
  237. * If no event is pending, the polling loop is called a second time because the
  238. * status of some file descriptors could have changed in the time between
  239. * polling their state and adding the event queue. This prevents a race
  240. * condition.
  241. *
  242. * If still no events are pending we call NutEventWait() on our event queue to
  243. * go to sleep until any driver is waking us up or the timeout expired.
  244. *
  245. * The third polling loop then cleans up the wait queues of the file descriptors
  246. * by removing our event queue from the list. Further more the status of the
  247. * file descriptors is finally gathered.
  248. *
  249. * \param n the highest-numbered file descriptor in any of the three
  250. * sets plus 1
  251. * \param flags Flag set array which represents the different type of
  252. * monitoring requests for this file descriptor (read / write /
  253. * exception)
  254. * \param rflags Flag set which will be filled with the events that had been
  255. * recorded in the meantime
  256. * \param to Upper bound on the amount of time elapsed before select()
  257. * returns. If NULL, select can block indefinitely
  258. *
  259. * \return On success, the total number filedescriptor whos state has changed
  260. * is returned. 0 means a timeout.
  261. * On error, -1 is returned, and errno is set appropriately. The content
  262. * of the three sets will be undefined.
  263. */
  264. static inline int do_select(int n, uint8_t *flags, uint8_t *rflags, uint32_t to)
  265. {
  266. int count;
  267. HANDLE wq = NULL;
  268. /* Check if any events are just set and add our event queue to the waitqueu list of this device */
  269. count = select_scan(n, flags, rflags, &wq, SELECT_CMD_INIT);
  270. if (count == 0) {
  271. /* We just got no result, so let's wait here. Otherwise skip waiting and continue with cleanup... */
  272. /* Call select_scan one more time, as events could have happend in the meanwhile. This should prevent
  273. reace conditions as either the scan will return the event or the event queue gets signalled in the
  274. meantime.
  275. TODO: Do we need this second scan?
  276. */
  277. count = select_scan(n, flags, rflags, NULL, SELECT_CMD_NOP);
  278. if (count == 0) {
  279. /* No events happend so far, so let's go to sleep or directly wake up again if we got signalled
  280. in the meantime
  281. */
  282. NutEventWait(&wq, to);
  283. }
  284. }
  285. /* Finally gather all set events and clean up by removing our even queue from the waitqueue lists */
  286. count = select_scan(n, flags, rflags, &wq, SELECT_CMD_CLEANUP);
  287. return count;
  288. }
  289. /*!
  290. * \brief Examine the status of file descriptors
  291. *
  292. * select() allows to monitor multiple file descriptors and wait until one or
  293. * more descriptors become ready for the selected I/O operation.
  294. * e.g. characters are available for reading of buffer space is available for
  295. * writing. In this context ready means that the it is possible to perform
  296. * the corresponding I/O operation without blocking.
  297. *
  298. * Three independend sets of file descriptors can be watched. Each of them can
  299. * be NULL. You can even call select with all three sets set to NULL, which
  300. * results in a sleep of the given timeout.
  301. * The file descriptor sets are modified in place and pon exit will represend
  302. * the file descriptors that actually changed status.
  303. *
  304. * \param n the highest-numbered file descriptor in any of the three sets plus 1
  305. * \param rfds Set of filedescriptors to be watched if a read() call will
  306. * not block (e.g. incomming characters become available for reading)
  307. * \param wfds Set of filedescriptors to be watched if a write() call will not block.
  308. * \param exfds Set of filedescriptors to be watched for exceptions.
  309. * \param timeout Upper bound on the amount of time elapsed before select()
  310. * returns. If NULL, select can block indefinitely
  311. *
  312. * \return On success, the total number filedescriptor whos state has changed
  313. * is returned. 0 means a timeout.
  314. * On error, -1 is returned, and errno is set appropriately. The content
  315. * of the three sets will be undefined.
  316. */
  317. int select(int n, fd_set *rfds, fd_set *wfds, fd_set *exfds, struct timeval *timeout)
  318. {
  319. int rc;
  320. uint32_t to;
  321. int i;
  322. int max;
  323. uint_fast8_t flags;
  324. uint8_t *fdflags;
  325. uint8_t *rfdflags;
  326. uint_fast8_t bits;
  327. int fd;
  328. if ((n < 0) || (n > FD_SETSIZE)) {
  329. errno = EINVAL;
  330. return -1;
  331. }
  332. to = NUT_WAIT_INFINITE;
  333. if (timeout) {
  334. if (timeout->tv_sec > MAX_TIMEOUT_SEC) {
  335. errno = EINVAL;
  336. return -1;
  337. }
  338. /* Calculate the timeout in ms, if given */
  339. to = ((timeout->tv_sec * 1000) + ((timeout->tv_usec + 500)/1000));
  340. if (to == 0) {
  341. /* Wait at least one ms, as 0 == NUT_WAIT_INFINITE */
  342. to = 1;
  343. }
  344. }
  345. /* Check the file descriptor sets for validity */
  346. max = -1;
  347. for (i = 0; i < FD_SETSIZE / 8; i++) {
  348. bits = 0;
  349. if (rfds != NULL)
  350. bits |= rfds->fd_bits[i];
  351. if (wfds != NULL)
  352. bits |= wfds->fd_bits[i];
  353. if (exfds != NULL)
  354. bits |= exfds->fd_bits[i];
  355. fd = i << 3; /* fd = i*8 */
  356. for (; bits; bits >>= 1, fd++) {
  357. if (fd >= n) {
  358. goto start_select;
  359. }
  360. if (!bits & 0x01) {
  361. continue;
  362. }
  363. if ((!__fds[fd]) || (__fds[fd] == NUTFILE_EOF)) {
  364. errno = EBADF;
  365. return -1;
  366. }
  367. /* Update the highest file descriptor number */
  368. max = fd;
  369. }
  370. }
  371. start_select:
  372. /* Update n to the calculated value, which evaluates to 0 if the sets are empty */
  373. n = max + 1;
  374. /* allocate memory for the file descriptor flag list */
  375. fdflags = malloc(n * sizeof(uint8_t));
  376. /* allocate clean memory for the file descriptor return flag list */
  377. rfdflags = calloc(n, sizeof(uint8_t));
  378. for (fd = 0; fd < n; fd ++) {
  379. /* Gather the flags by checking each descriptor set if the current fd is part of it */
  380. flags = 0;
  381. if ((rfds != NULL) && FD_ISSET(fd, rfds))
  382. flags |= WQ_FLAG_READ;
  383. if ((wfds != NULL) && FD_ISSET(fd, wfds))
  384. flags |= WQ_FLAG_WRITE;
  385. if ((exfds != NULL) && FD_ISSET(fd, exfds))
  386. flags |= WQ_FLAG_EXCEPT;
  387. /* and safe the flags into the flag list */
  388. fdflags[fd] = flags;
  389. }
  390. /* call do_select, which finally implemens the select functionality */
  391. rc = do_select(n, fdflags, rfdflags, to);
  392. /* Clean up the sets */
  393. if (rfds != NULL)
  394. FD_ZERO(rfds);
  395. if (wfds != NULL)
  396. FD_ZERO(wfds);
  397. if (exfds != NULL)
  398. FD_ZERO(exfds);
  399. /* And safe the results */
  400. for (fd = 0; fd < n; fd++) {
  401. flags = rfdflags[fd];
  402. if ((rfds != NULL) && (flags & WQ_FLAG_READ))
  403. FD_SET(fd, rfds);
  404. if ((wfds != NULL) && (flags & WQ_FLAG_WRITE))
  405. FD_SET(fd, wfds);
  406. if ((exfds != NULL) && (flags & WQ_FLAG_EXCEPT))
  407. FD_SET(fd, exfds);
  408. }
  409. /* Free allocated memory */
  410. free(fdflags);
  411. free(rfdflags);
  412. return rc;
  413. }
  414. #endif
  415. /*@}*/