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