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