add rudimentary protocol handling
[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 void init_virtual_device(struct device *dev, const struct device_type *type, const char *name)
89 {
90         assert(dev);
91         assert(type);
92
93         fprintf(stderr, "Initialize interface '%s'\n", dev->ifname);
94         INIT_LIST_HEAD(&dev->users);
95         dev->type = type;
96
97         if (name)
98                 strncpy(dev->ifname, name, IFNAMSIZ);
99 }
100
101 int init_device(struct device *dev, const struct device_type *type, const char *ifname)
102 {
103         int ret;
104
105         init_virtual_device(dev, type, ifname);
106
107         if (!dev->set_state)
108                 dev->set_state = set_device_state;
109
110         dev->avl.key = dev->ifname;
111
112         ret = avl_insert(&devices, &dev->avl);
113         if (ret < 0)
114                 return ret;
115
116         check_device_state(dev);
117
118         return 0;
119 }
120
121 struct device *get_device(const char *name, bool create)
122 {
123         static const struct device_type simple_type = {
124                 .name = "Device",
125                 .check_state = system_if_check,
126                 .free = free_device,
127         };
128         struct device *dev;
129
130
131         if (strchr(name, '.'))
132                 return get_vlan_device_chain(name, create);
133
134         dev = avl_find_element(&devices, name, dev, avl);
135         if (dev)
136                 return dev;
137
138         if (!create)
139                 return NULL;
140
141         dev = calloc(1, sizeof(*dev));
142         init_device(dev, &simple_type, name);
143
144         return dev;
145 }
146
147 void cleanup_device(struct device *dev)
148 {
149         struct device_user *dep, *tmp;
150
151         fprintf(stderr, "Clean up interface '%s'\n", dev->ifname);
152         list_for_each_entry_safe(dep, tmp, &dev->users, list) {
153                 if (!dep->cb)
154                         continue;
155
156                 dep->cb(dep, DEV_EVENT_REMOVE);
157         }
158
159         if (dev->avl.key)
160                 avl_delete(&devices, &dev->avl);
161 }
162
163 void set_device_present(struct device *dev, bool state)
164 {
165         if (dev->present == state)
166                 return;
167
168         DPRINTF("Device '%s' %s present\n", dev->ifname, state ? "is now" : "is no longer" );
169         dev->present = state;
170         broadcast_device_event(dev, state ? DEV_EVENT_ADD : DEV_EVENT_REMOVE);
171 }
172
173 void add_device_user(struct device_user *dep, struct device *dev)
174 {
175         dep->dev = dev;
176         list_add(&dep->list, &dev->users);
177         if (dep->cb && dev->present) {
178                 dep->cb(dep, DEV_EVENT_ADD);
179                 if (dev->active)
180                         dep->cb(dep, DEV_EVENT_UP);
181         }
182 }
183
184 void remove_device_user(struct device_user *dep)
185 {
186         struct device *dev = dep->dev;
187
188         list_del(&dep->list);
189
190         if (list_empty(&dev->users)) {
191                 /* all references have gone away, remove this interface */
192                 dev->type->free(dev);
193         }
194
195         dep->dev = NULL;
196 }