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