refactor: one more step towards arch-agnostic design
[lunaix-os.git] / lunaix-os / kernel / device / device.c
1 #include <klibc/stdio.h>
2 #include <lunaix/device.h>
3 #include <lunaix/fs.h>
4 #include <lunaix/fs/twifs.h>
5 #include <lunaix/ioctl.h>
6 #include <lunaix/mm/valloc.h>
7 #include <lunaix/spike.h>
8 #include <lunaix/syscall.h>
9 #include <lunaix/syscall_utils.h>
10
11 static DEFINE_LLIST(root_list);
12
13 static volatile dev_t devid = 0;
14
15 struct device*
16 device_add(struct device* parent,
17            void* underlay,
18            char* name_fmt,
19            u32_t type,
20            va_list args)
21 {
22     struct device* dev = vzalloc(sizeof(struct device));
23
24     if (parent) {
25         assert((parent->dev_type & DEV_MSKIF) == DEV_IFCAT);
26         llist_append(&parent->children, &dev->siblings);
27     } else {
28         llist_append(&root_list, &dev->siblings);
29     }
30
31     size_t strlen =
32       __ksprintf_internal(dev->name_val, name_fmt, DEVICE_NAME_SIZE, args);
33
34     dev->magic = DEV_STRUCT_MAGIC;
35     dev->dev_id = devid++;
36     dev->name = HSTR(dev->name_val, strlen);
37     dev->parent = parent;
38     dev->underlay = underlay;
39     dev->dev_type = type;
40
41     hstr_rehash(&dev->name, HSTR_FULL_HASH);
42     llist_init_head(&dev->children);
43
44     return dev;
45 }
46
47 struct device*
48 device_addseq(struct device* parent, void* underlay, char* name_fmt, ...)
49 {
50     va_list args;
51     va_start(args, name_fmt);
52
53     struct device* dev =
54       device_add(parent, underlay, name_fmt, DEV_IFSEQ, args);
55
56     va_end(args);
57     return dev;
58 }
59
60 struct device*
61 device_addvol(struct device* parent, void* underlay, char* name_fmt, ...)
62 {
63     va_list args;
64     va_start(args, name_fmt);
65
66     struct device* dev =
67       device_add(parent, underlay, name_fmt, DEV_IFVOL, args);
68
69     va_end(args);
70     return dev;
71 }
72
73 struct device*
74 device_addcat(struct device* parent, char* name_fmt, ...)
75 {
76     va_list args;
77     va_start(args, name_fmt);
78
79     struct device* dev = device_add(parent, NULL, name_fmt, DEV_IFCAT, args);
80
81     va_end(args);
82     return dev;
83 }
84
85 struct device*
86 device_getbyid(struct llist_header* devlist, dev_t id)
87 {
88     devlist = devlist ? devlist : &root_list;
89     struct device *pos, *n;
90     llist_for_each(pos, n, devlist, siblings)
91     {
92         if (pos->dev_id == id) {
93             return pos;
94         }
95     }
96
97     return NULL;
98 }
99
100 struct device*
101 device_getbyhname(struct device* root_dev, struct hstr* name)
102 {
103     struct llist_header* devlist = root_dev ? &root_dev->children : &root_list;
104     struct device *pos, *n;
105     llist_for_each(pos, n, devlist, siblings)
106     {
107         if (HSTR_EQ(&pos->name, name)) {
108             return pos;
109         }
110     }
111
112     return NULL;
113 }
114
115 struct device*
116 device_getbyname(struct device* root_dev, const char* name, size_t len)
117 {
118     struct hstr hname = HSTR(name, len);
119     hstr_rehash(&hname, HSTR_FULL_HASH);
120
121     return device_getbyhname(root_dev, &hname);
122 }
123
124 void
125 device_remove(struct device* dev)
126 {
127     llist_delete(&dev->siblings);
128     vfree(dev);
129 }
130
131 struct device*
132 device_getbyoffset(struct device* root_dev, int offset)
133 {
134     struct llist_header* devlist = root_dev ? &root_dev->children : &root_list;
135     struct device *pos, *n;
136     int off = 0;
137     llist_for_each(pos, n, devlist, siblings)
138     {
139         if (off++ >= offset) {
140             return pos;
141         }
142     }
143     return NULL;
144 }
145
146 __DEFINE_LXSYSCALL3(int, ioctl, int, fd, int, req, va_list, args)
147 {
148     int errno;
149     struct v_fd* fd_s;
150     if ((errno = vfs_getfd(fd, &fd_s))) {
151         goto done;
152     }
153
154     struct device* dev = (struct device*)fd_s->file->inode->data;
155     if (dev->magic != DEV_STRUCT_MAGIC) {
156         errno = ENODEV;
157         goto done;
158     }
159
160     if (!dev->exec_cmd) {
161         errno = EINVAL;
162         goto done;
163     }
164
165     errno = dev->exec_cmd(dev, req, args);
166
167 done:
168     return DO_STATUS_OR_RETURN(errno);
169 }