a8f369564fbd59a42616f83ce8ff46f2c15d4501
[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; /* always repeat by default */
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                         if (fdev->itf->unref)
80                                 fdev->itf->unref(fdev->closure_itf);
81                 }
82                 if (fdev->refcount)
83                         close(fdev->fd);
84                 free(fdev);
85         }
86 }
87
88 int fdev_fd(const struct fdev *fdev)
89 {
90         return fdev->fd;
91 }
92
93 uint32_t fdev_events(const struct fdev *fdev)
94 {
95         return fdev->events;
96 }
97
98 int fdev_repeat(const struct fdev *fdev)
99 {
100         return fdev->repeat;
101 }
102
103 int fdev_autoclose(const struct fdev *fdev)
104 {
105         return 1 & fdev->refcount;
106 }
107
108 static inline int is_active(struct fdev *fdev)
109 {
110         return fdev->repeat && fdev->callback;
111 }
112
113 static inline void update_activity(struct fdev *fdev, int old_active)
114 {
115         if (is_active(fdev)) {
116                 if (!old_active)
117                         fdev->itf->enable(fdev->closure_itf, fdev);
118         } else {
119                 if (old_active)
120                         fdev->itf->disable(fdev->closure_itf, fdev);
121         }
122 }
123
124 void fdev_set_callback(struct fdev *fdev, void (*callback)(void*,uint32_t,struct fdev*), void *closure)
125 {
126         int oa;
127
128         oa = is_active(fdev);
129         fdev->callback = callback;
130         fdev->closure_callback = closure;
131         update_activity(fdev, oa);
132 }
133
134 void fdev_set_events(struct fdev *fdev, uint32_t events)
135 {
136         if (events != fdev->events) {
137                 fdev->events = events;
138                 if (is_active(fdev))
139                         fdev->itf->update(fdev->closure_itf, fdev);
140         }
141 }
142
143 void fdev_set_repeat(struct fdev *fdev, int count)
144 {
145         int oa;
146
147         oa = is_active(fdev);
148         fdev->repeat = count;
149         update_activity(fdev, oa);
150 }
151
152 void fdev_set_autoclose(struct fdev *fdev, int autoclose)
153 {
154         if (autoclose)
155                 fdev->refcount |= (unsigned)1;
156         else
157                 fdev->refcount &= ~(unsigned)1;
158 }
159