1 /*
2  * Copyright (C) 2020-2021 Alibaba Group Holding Limited
3  */
4 
5 #include <stdbool.h>
6 #include <stdio.h>
7 #include <limits.h>
8 #include <string.h>
9 #include <errno.h>
10 #include "aos/kernel.h"
11 #include "aos/vfs.h"
12 #include <select.h>
13 
14 #ifndef CONFIG_AOS_LWIP
15 #define CONFIG_NO_TCPIP
16 #endif
17 
18 #ifdef CONFIG_NO_TCPIP
19 struct poll_arg {
20     aos_sem_t sem;
21 };
22 
vfs_poll_notify(void * fd,void * arg)23 static void vfs_poll_notify(void *fd, void *arg)
24 {
25     struct poll_arg *parg = arg;
26     aos_sem_signal(&parg->sem);
27 }
28 
wait_io(int maxfd,fd_set * rfds,fd_set * wfds,fd_set * efds,struct poll_arg * parg,int timeout)29 static int wait_io(int maxfd, fd_set *rfds, fd_set *wfds, fd_set *efds, struct poll_arg *parg, int timeout)
30 {
31     timeout = timeout >= 0 ? timeout : AOS_WAIT_FOREVER;
32     aos_sem_wait(&parg->sem, timeout);
33     return 0;
34 }
35 
init_parg(struct poll_arg * parg)36 static int init_parg(struct poll_arg *parg)
37 {
38     aos_sem_new(&parg->sem,  0);
39     return 0;
40 }
41 
deinit_parg(struct poll_arg * parg)42 static void deinit_parg(struct poll_arg *parg)
43 {
44     aos_sem_free(&parg->sem);
45 }
46 #else
47 #include <sys/socket.h>
48 struct poll_arg {
49     int efd;
50 };
51 
52 extern int lwip_write(int s, const void *dataptr, size_t size);
53 extern int lwip_select(int maxfdp1, fd_set *readset, fd_set *writeset, fd_set *exceptset,
54                        struct timeval *timeout);
55 extern int lwip_eventfd(unsigned int initval, int flags);
56 extern int lwip_close(int s);
57 
vfs_poll_notify(void * fd,void * arg)58 static void vfs_poll_notify(void *fd, void *arg)
59 {
60     struct poll_arg *parg = arg;
61     uint64_t val = 1;
62     lwip_write(parg->efd, &val, sizeof val);
63 }
64 
init_parg(struct poll_arg * parg)65 static int init_parg(struct poll_arg *parg)
66 {
67     int efd;
68     efd = lwip_eventfd(0, 0);
69 
70     if (efd < 0) {
71         errno = EINVAL;
72         return -1;
73     }
74 
75     parg->efd = efd;
76 
77     return 0;
78 }
79 
deinit_parg(struct poll_arg * parg)80 static void deinit_parg(struct poll_arg *parg)
81 {
82     lwip_close(parg->efd);
83 }
84 
wait_io(int maxfd,fd_set * rfds,fd_set * wfds,fd_set * efds,struct poll_arg * parg,int timeout)85 static int wait_io(int maxfd, fd_set *rfds, fd_set *wfds, fd_set *efds, struct poll_arg *parg, int timeout)
86 {
87     int ret;
88     struct timeval tv = {
89         .tv_sec  = timeout / 1000,
90         .tv_usec = (timeout % 1000) * 1000,
91     };
92 
93     FD_SET(parg->efd, rfds);
94     maxfd = parg->efd > maxfd ? parg->efd : maxfd;
95     ret = lwip_select(maxfd + 1, rfds, wfds, efds, timeout >= 0 ? &tv : NULL);
96 
97     /* return socketfd event num only ,so we sub event fd num */
98     if (ret > 0) {
99         if (FD_ISSET(parg->efd, rfds)) {
100             ret--;
101         }
102         if (FD_ISSET(parg->efd, wfds)) {
103             errno = EINVAL;
104             return -1;
105         }
106         if (FD_ISSET(parg->efd, efds)) {
107             errno = EINVAL;
108             return -1;
109         }
110     }
111     return ret;
112 }
113 #endif
114 
pre_poll(struct pollfd * fds,int nfds,fd_set * rfds,fd_set * wfds,fd_set * efds,void * parg)115 static int pre_poll(struct pollfd *fds, int nfds, fd_set *rfds, fd_set *wfds, fd_set *efds, void *parg)
116 {
117     int i;
118     int maxfd = 0;
119 
120     for (i = 0; i < nfds; i++) {
121         struct pollfd *pfd = &fds[i];
122 
123         pfd->revents = 0;
124     }
125 
126     for (i = 0; i < nfds; i++) {
127         struct pollfd *pfd = &fds[i];
128         if (pfd->fd < aos_vfs_fd_offset_get()) {
129             if (pfd->fd > FD_SETSIZE - 1) {
130                 errno = EINVAL;
131                 return -1;
132             }
133             if (pfd->events & POLLIN) {
134                 FD_SET(pfd->fd, rfds);
135             }
136             if (pfd->events & POLLOUT) {
137                 FD_SET(pfd->fd, wfds);
138             }
139             if (pfd->events & POLLERR) {
140                 FD_SET(pfd->fd, efds);
141             }
142             maxfd = pfd->fd > maxfd ? pfd->fd : maxfd;
143         } else {
144             if (aos_do_pollfd(pfd->fd, true, vfs_poll_notify, pfd, parg) == -ENOENT) {
145                 errno = ENOENT;
146                 return -1;
147             }
148         }
149     }
150 
151     return maxfd;
152 }
153 
post_poll(struct pollfd * fds,int nfds)154 static int post_poll(struct pollfd *fds, int nfds)
155 {
156     int j;
157     int ret = 0;
158 
159     for (j = 0; j < nfds; j++) {
160         struct pollfd *pfd = &fds[j];
161 
162         if (pfd->fd < aos_vfs_fd_offset_get()) {
163             continue;
164         }
165 
166         if (aos_do_pollfd(pfd->fd, false, NULL, NULL, NULL) == -ENOENT) {
167             continue;
168         }
169 
170         if (pfd->revents) {
171             ret ++;
172         }
173     }
174 
175     return ret;
176 }
177 
aos_poll(struct pollfd * fds,int nfds,int timeout)178 int aos_poll(struct pollfd *fds, int nfds, int timeout)
179 {
180     fd_set rfds;
181     fd_set wfds;
182     fd_set efds;
183 
184     int ret = 0;
185     int nset = 0;
186     struct poll_arg parg;
187 
188     if (init_parg(&parg) < 0) {
189         return -1;
190     }
191 
192     FD_ZERO(&rfds);
193     FD_ZERO(&wfds);
194     FD_ZERO(&efds);
195 
196     ret = pre_poll(fds, nfds, &rfds, &wfds, &efds, &parg);
197 
198     if (ret < 0) {
199         goto check_poll;
200     }
201 
202     ret = wait_io(ret, &rfds, &wfds, &efds, &parg, timeout);
203 
204     if (ret > 0) {
205         int i;
206 
207         for (i = 0; i < nfds; i++) {
208             struct pollfd *pfd = &fds[i];
209 
210             if (FD_ISSET(pfd->fd, &rfds)) {
211                 pfd->revents |= POLLIN;
212             }
213             if (FD_ISSET(pfd->fd, &wfds)) {
214                 pfd->revents |= POLLOUT;
215             }
216             if (FD_ISSET(pfd->fd, &efds)) {
217                 pfd->revents |= POLLERR;
218             }
219         }
220         nset += ret;
221     }
222 
223 check_poll:
224     nset += post_poll(fds, nfds);
225 
226     deinit_parg(&parg);
227 
228     return ret < 0 ? -1 : nset;
229 }
230