fdev: Introduce fdev for file event handling
[src/app-framework-binder.git] / src / fdev.c
1 /*
2  * Copyright (C) 2018 "IoT.bzh"
3  * Author José Bollo <jose.bollo@iot.bzh>
4  *
5  * Licensed under the Apache License, Version 2.0 (the "License");
6  * you may not use this file except in compliance with the License.
7  * You may obtain a copy of the License at
8  *
9  *   http://www.apache.org/licenses/LICENSE-2.0
10  *
11  * Unless required by applicable law or agreed to in writing, software
12  * distributed under the License is distributed on an "AS IS" BASIS,
13  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14  * See the License for the specific language governing permissions and
15  * limitations under the License.
16  */
17
18 #include <stdint.h>
19 #include <stdlib.h>
20 #include <unistd.h>
21 #include <errno.h>
22
23 #define FDEV_PROVIDER
24 #include "fdev.h"
25
26 struct fdev
27 {
28         int fd;
29         uint32_t events;
30         int repeat;
31         unsigned refcount;
32         struct fdev_itf *itf;
33         void *closure_itf;
34         void (*callback)(void*,uint32_t,struct fdev*);
35         void *closure_callback;
36 };
37
38 struct fdev *fdev_create(int fd)
39 {
40         struct fdev *fdev;
41
42         fdev = calloc(1, sizeof *fdev);
43         if (!fdev)
44                 errno = ENOMEM;
45         else {
46                 fdev->fd = fd;
47                 fdev->refcount = 3; /* set autoclose by default */
48                 fdev->repeat = -1;
49         }
50         return fdev;
51 }
52
53 void fdev_set_itf(struct fdev *fdev, struct fdev_itf *itf, void *closure_itf)
54 {
55         fdev->itf = itf;
56         fdev->closure_itf = closure_itf;
57 }
58
59 void fdev_dispatch(struct fdev *fdev, uint32_t events)
60 {
61         if (fdev->repeat > 0 && !--fdev->repeat && fdev->itf)
62                 fdev->itf->disable(fdev->closure_itf, fdev);
63         if (fdev->callback)
64                 fdev->callback(fdev->closure_callback, events, fdev);
65 }
66
67 struct fdev *fdev_addref(struct fdev *fdev)
68 {
69         if (fdev)
70                 __atomic_add_fetch(&fdev->refcount, 2, __ATOMIC_RELAXED);
71         return fdev;
72 }
73
74 void fdev_unref(struct fdev *fdev)
75 {
76         if (fdev && __atomic_sub_fetch(&fdev->refcount, 2, __ATOMIC_RELAXED) <= 1) {
77                 if (fdev->itf) {
78                         fdev->itf->disable(fdev->closure_itf, fdev);
79                         fdev->itf->unref(fdev->closure_itf);
80                 }
81                 if (fdev->refcount)
82                         close(fdev->fd);
83                 free(fdev);
84         }
85 }
86
87 int fdev_fd(const struct fdev *fdev)
88 {
89         return fdev->fd;
90 }
91
92 uint32_t fdev_events(const struct fdev *fdev)
93 {
94         return fdev->events;
95 }
96
97 int fdev_repeat(const struct fdev *fdev)
98 {
99         return fdev->repeat;
100 }
101
102 int fdev_autoclose(const struct fdev *fdev)
103 {
104         return 1 & fdev->refcount;
105 }
106
107 static inline int is_active(struct fdev *fdev)
108 {
109         return fdev->repeat && fdev->callback;
110 }
111
112 static inline void update_activity(struct fdev *fdev, int old_active)
113 {
114         if (is_active(fdev)) {
115                 if (!old_active)
116                         fdev->itf->enable(fdev->closure_itf, fdev);
117         } else {
118                 if (old_active)
119                         fdev->itf->disable(fdev->closure_itf, fdev);
120         }
121 }
122
123 void fdev_set_callback(struct fdev *fdev, void (*callback)(void*,uint32_t,struct fdev*), void *closure)
124 {
125         int oa;
126
127         oa = is_active(fdev);
128         fdev->callback = callback;
129         fdev->closure_callback = closure;
130         update_activity(fdev, oa);
131 }
132
133 void fdev_set_events(struct fdev *fdev, uint32_t events)
134 {
135         if (events != fdev->events) {
136                 fdev->events = events;
137                 if (is_active(fdev))
138                         fdev->itf->enable(fdev->closure_itf, fdev);
139         }
140 }
141
142 void fdev_set_repeat(struct fdev *fdev, int count)
143 {
144         int oa;
145
146         oa = is_active(fdev);
147         fdev->repeat = count;
148         update_activity(fdev, oa);
149 }
150
151 void fdev_set_autoclose(struct fdev *fdev, int autoclose)
152 {
153         if (autoclose)
154                 fdev->refcount |= 1;
155         else
156                 fdev->refcount &= -2;
157 }
158