31fc4673c9ce2867f9e026bf358fd2995d10a0b7
[project/netifd.git] / device.c
1 #include <string.h>
2 #include <stdlib.h>
3 #include <stdio.h>
4 #include <assert.h>
5
6 #include <libubox/uapi.h>
7
8 #include "netifd.h"
9 #include "system.h"
10
11 static struct avl_tree devices;
12
13 static void API_CTOR dev_init(void)
14 {
15         avl_init(&devices, avl_strcmp, false, NULL);
16 }
17
18 static void free_simple_device(struct device *dev)
19 {
20         cleanup_device(dev);
21         free(dev);
22 }
23
24 static void broadcast_device_event(struct device *dev, enum device_event ev)
25 {
26         struct device_user *dep, *tmp;
27
28         list_for_each_entry_safe(dep, tmp, &dev->users, list) {
29                 if (!dep->cb)
30                         continue;
31
32                 dep->cb(dep, ev);
33         }
34 }
35
36 static int set_device_state(struct device *dev, bool state)
37 {
38         if (state)
39                 system_if_up(dev);
40         else
41                 system_if_down(dev);
42
43         return 0;
44 }
45
46 int claim_device(struct device *dev)
47 {
48         int ret;
49
50         DPRINTF("claim device %s, new refcount: %d\n", dev->ifname, dev->active + 1);
51         if (++dev->active != 1)
52                 return 0;
53
54         broadcast_device_event(dev, DEV_EVENT_SETUP);
55         ret = dev->set_state(dev, true);
56         if (ret == 0)
57                 broadcast_device_event(dev, DEV_EVENT_UP);
58         else
59                 dev->active = 0;
60
61         return ret;
62 }
63
64 void release_device(struct device *dev)
65 {
66         dev->active--;
67         DPRINTF("release device %s, new refcount: %d\n", dev->ifname, dev->active);
68         assert(dev->active >= 0);
69
70         if (dev->active)
71                 return;
72
73         broadcast_device_event(dev, DEV_EVENT_TEARDOWN);
74         dev->set_state(dev, false);
75         broadcast_device_event(dev, DEV_EVENT_DOWN);
76 }
77
78 int check_device_state(struct device *dev)
79 {
80         if (!dev->type->check_state)
81                 return 0;
82
83         return dev->type->check_state(dev);
84 }
85
86 void init_virtual_device(struct device *dev, const struct device_type *type, const char *name)
87 {
88         assert(dev);
89         assert(type);
90
91         if (name)
92                 strncpy(dev->ifname, name, IFNAMSIZ);
93
94         fprintf(stderr, "Initialize device '%s'\n", dev->ifname);
95         INIT_LIST_HEAD(&dev->users);
96         dev->type = type;
97 }
98
99 int init_device(struct device *dev, const struct device_type *type, const char *ifname)
100 {
101         int ret;
102
103         init_virtual_device(dev, type, ifname);
104
105         if (!dev->set_state)
106                 dev->set_state = set_device_state;
107
108         dev->avl.key = dev->ifname;
109
110         ret = avl_insert(&devices, &dev->avl);
111         if (ret < 0)
112                 return ret;
113
114         check_device_state(dev);
115
116         return 0;
117 }
118
119 struct device *get_device(const char *name, bool create)
120 {
121         static const struct device_type simple_type = {
122                 .name = "Device",
123                 .check_state = system_if_check,
124                 .free = free_simple_device,
125         };
126         struct device *dev;
127
128
129         if (strchr(name, '.'))
130                 return get_vlan_device_chain(name, create);
131
132         dev = avl_find_element(&devices, name, dev, avl);
133         if (dev)
134                 return dev;
135
136         if (!create)
137                 return NULL;
138
139         dev = calloc(1, sizeof(*dev));
140         init_device(dev, &simple_type, name);
141
142         return dev;
143 }
144
145 void cleanup_device(struct device *dev)
146 {
147         struct device_user *dep, *tmp;
148
149         fprintf(stderr, "Clean up device '%s'\n", dev->ifname);
150         list_for_each_entry_safe(dep, tmp, &dev->users, list) {
151                 if (!dep->cb)
152                         continue;
153
154                 dep->cb(dep, DEV_EVENT_REMOVE);
155         }
156
157         if (dev->avl.key)
158                 avl_delete(&devices, &dev->avl);
159 }
160
161 void set_device_present(struct device *dev, bool state)
162 {
163         if (dev->present == state)
164                 return;
165
166         DPRINTF("Device '%s' %s present\n", dev->ifname, state ? "is now" : "is no longer" );
167         dev->present = state;
168         broadcast_device_event(dev, state ? DEV_EVENT_ADD : DEV_EVENT_REMOVE);
169 }
170
171 void add_device_user(struct device_user *dep, struct device *dev)
172 {
173         dep->dev = dev;
174         list_add(&dep->list, &dev->users);
175         if (dep->cb && dev->present) {
176                 dep->cb(dep, DEV_EVENT_ADD);
177                 if (dev->active)
178                         dep->cb(dep, DEV_EVENT_UP);
179         }
180 }
181
182 void remove_device_user(struct device_user *dep)
183 {
184         struct device *dev = dep->dev;
185
186         list_del(&dep->list);
187
188         if (list_empty(&dev->users)) {
189                 /* all references have gone away, remove this device */
190                 free_device(dev);
191         }
192
193         dep->dev = NULL;
194 }
195
196 void
197 cleanup_devices(void)
198 {
199         struct device *dev, *tmp;
200
201         avl_for_each_element_safe(&devices, dev, avl, tmp) {
202                 if (!list_empty(&dev->users))
203                         continue;
204
205                 free_device(dev);
206         }
207 }