1 /*
2  * Copyright (C) 2015-2017 Alibaba Group Holding Limited
3  */
4 
5 #include <stdbool.h>
6 #include <stdlib.h>
7 #include <string.h>
8 #include "aos/kernel.h"
9 
10 #include "aos/list.h"
11 #include "aos/vfs.h"
12 #include "poll.h"
13 
14 #define CACHE_COUNT 5
15 
16 
17 #define MK_CMD(c, l) ((l << 4) | (c))
18 #define _GET_LEN(cmd) ((cmd) >> 4)
19 #define _GET_CMD(cmd) ((cmd) & 0xf)
20 
21 typedef struct {
22     aos_mutex_t    mutex;
23     poll_notify_t  poll_cb;
24     struct pollfd *fd;
25     void          *poll_data;
26     int            counter;
27     dlist_t        bufs;
28     int            cache_count;
29     dlist_t        buf_cache;
30 } event_dev_t;
31 
32 typedef struct {
33     dlist_t node;
34     size_t len;
35     char buf[];
36 } dev_event_t;
37 
event_open(inode_t * node,file_t * file)38 static int event_open(inode_t *node, file_t *file)
39 {
40     event_dev_t *pdev = (event_dev_t *)aos_malloc(sizeof * pdev);
41     memset(pdev, 0, sizeof (*pdev));
42     aos_mutex_new(&pdev->mutex);
43     dlist_init(&pdev->bufs);
44     dlist_init(&pdev->buf_cache);
45     file->f_arg = pdev;
46     return 0;
47 }
48 
event_close(file_t * file)49 static int event_close(file_t *file)
50 {
51     event_dev_t *pdev = file->f_arg;
52     aos_mutex_free(&pdev->mutex);
53 
54     while (!dlist_empty(&pdev->bufs)) {
55         dlist_t *n = pdev->bufs.next;
56         dlist_del(n);
57         aos_free(n);
58     }
59 
60     while (!dlist_empty(&pdev->buf_cache)) {
61         dlist_t *n = pdev->buf_cache.next;
62         dlist_del(n);
63         aos_free(n);
64     }
65 
66     aos_free(pdev);
67 
68     return 0;
69 }
70 
_event_write(file_t * f,const void * buf,size_t len,bool urgent)71 static ssize_t _event_write(file_t *f, const void *buf, size_t len, bool urgent)
72 {
73     event_dev_t *pdev = f->f_arg;
74     aos_mutex_lock(&pdev->mutex, AOS_WAIT_FOREVER);
75     ssize_t ret = len;
76 
77     dev_event_t *evt;
78     evt = (dev_event_t *)pdev->buf_cache.next;
79 
80     if (pdev->cache_count > 0 && evt->len == len) {
81         dlist_del(&evt->node);
82         pdev->cache_count --;
83     } else {
84         evt = (dev_event_t *)aos_malloc(sizeof(*evt) + len);
85     }
86 
87     if (evt == NULL) {
88         ret = -1;
89         goto out;
90     }
91 
92     pdev->counter ++;
93 
94     evt->len = len;
95     memcpy(evt->buf, buf, len);
96     if (urgent) {
97         dlist_add(&evt->node, &pdev->bufs);
98     } else {
99         dlist_add_tail(&evt->node, &pdev->bufs);
100     }
101 
102     if (pdev->poll_cb != NULL) {
103         pdev->fd->revents |= POLLIN;
104         pdev->poll_cb(pdev->fd, pdev->poll_data);
105     }
106 out:
107     aos_mutex_unlock(&pdev->mutex);
108     return ret;
109 }
110 
event_write(file_t * f,const void * buf,size_t len)111 static ssize_t event_write(file_t *f, const void *buf, size_t len)
112 {
113     return _event_write(f, buf, len, false);
114 }
115 
event_ioctl(file_t * f,int cmd,unsigned long arg)116 static int event_ioctl(file_t *f, int cmd, unsigned long arg)
117 {
118     int len = _GET_LEN(cmd);
119     void *buf = (void *)arg;
120     cmd = _GET_CMD(cmd);
121     switch (cmd) {
122         case 1:
123             return _event_write(f, buf, len, false);
124         case 2:
125             return _event_write(f, buf, len, true);
126     }
127 
128     return -1;
129 }
130 
event_read(file_t * f,void * buf,size_t len)131 static ssize_t event_read(file_t *f, void *buf, size_t len)
132 {
133     event_dev_t *pdev = f->f_arg;
134     int cnt = pdev->counter;
135 
136     if (!cnt) {
137         return 0;
138     }
139 
140     aos_mutex_lock(&pdev->mutex, AOS_WAIT_FOREVER);
141 
142     dev_event_t *evt = (dev_event_t *)pdev->bufs.next;
143     dlist_del(&evt->node);
144     cnt = (len > evt->len) ? evt->len : len;
145     memcpy(buf, evt->buf, cnt);
146 
147     if (pdev->cache_count < CACHE_COUNT) {
148         dlist_add(&evt->node, &pdev->buf_cache);
149         pdev->cache_count ++;
150     } else {
151         aos_free(evt);
152     }
153 
154     if (-- pdev->counter == 0) {
155         if (pdev->fd != NULL) {
156             pdev->fd->revents &= ~POLLIN;
157         }
158     }
159     aos_mutex_unlock(&pdev->mutex);
160 
161     return cnt;
162 }
163 
event_poll(file_t * f,int setup,poll_notify_t notify,void * fd,void * opa)164 static int event_poll(file_t *f, int setup, poll_notify_t notify,
165                       void *fd, void *opa)
166 {
167     event_dev_t *pdev = f->f_arg;
168 
169     aos_mutex_lock(&pdev->mutex, AOS_WAIT_FOREVER);
170     if (!setup) {
171         pdev->poll_cb = NULL;
172         pdev->poll_data = NULL;
173         goto out;
174     }
175 
176     pdev->poll_cb = notify;
177     pdev->fd = (struct pollfd *)fd;
178     pdev->poll_data = opa;
179 
180     if (pdev->counter) {
181         pdev->fd->revents |= POLLIN;
182         (*notify)(fd, opa);
183     } else {
184         pdev->fd->revents &= ~POLLIN;
185     }
186 out:
187     aos_mutex_unlock(&pdev->mutex);
188 
189     return 0;
190 }
191 
192 static file_ops_t event_fops = {
193     .open = event_open,
194     .read = event_read,
195     .write = event_write,
196     .close = event_close,
197     .poll = event_poll,
198     .ioctl = event_ioctl,
199 };
200 
vfs_test_device_init(void)201 int vfs_test_device_init(void)
202 {
203     return aos_register_driver("/dev/test", &event_fops, NULL);
204 }
205 
vfs_test_device_deinit(void)206 int vfs_test_device_deinit(void)
207 {
208     return aos_unregister_driver("/dev/test");
209 }
210