xref: /minix/minix/servers/devman/device.c (revision 7f5f010b)
1 #include "devman.h"
2 #include "proto.h"
3 
4 
5 static struct devman_device*devman_dev_add_child(struct devman_device
6 	*parent, struct devman_device_info *devinf);
7 static struct devman_device *_find_dev(struct devman_device *dev, int
8 	dev_id);
9 static int devman_dev_add_info(struct devman_device *dev, struct
10 	devman_device_info_entry *entry, char *buf);
11 static int devman_event_read(char **ptr, size_t *len,off_t offset, void
12 	*data);
13 
14 static int devman_del_device(struct devman_device *dev);
15 
16 static int next_device_id = 1;
17 
18 static struct inode_stat default_dir_stat = {
19 	/* .mode  = */ S_IFDIR | S_IRUSR | S_IRGRP | S_IROTH,
20 	/* .uid   = */ 0,
21 	/* .gid   = */ 0,
22 	/* .size  = */ 0,
23 	/* .dev   = */ NO_DEV,
24 };
25 
26 static struct inode_stat default_file_stat = {
27 	/* .mode  = */ S_IFREG | S_IRUSR | S_IRGRP | S_IROTH,
28 	/* .uid   = */ 0,
29 	/* .gid   = */ 0,
30 	/* .size  = */ 0x1000,
31 	/* .dev   = */ NO_DEV,
32 };
33 
34 
35 static struct devman_device root_dev;
36 static struct devman_event_inode event_inode_data = {
37 	 TAILQ_HEAD_INITIALIZER(event_inode_data.event_queue),
38 };
39 static struct devman_inode event_inode;
40 
41 /*===========================================================================*
42  *           devman_generate_path                                            *
43  *===========================================================================*/
44 static int
45 devman_generate_path(char* buf, int len, struct devman_device *dev)
46 {
47 	int res =0;
48  	const char * name = ".";
49 	const char * sep = "/";
50 
51 	if (dev != NULL) {
52 		res = devman_generate_path(buf, len, dev->parent);
53 		if (res != 0) {
54 			return res;
55 		}
56 		name = get_inode_name(dev->inode.inode);
57 	} else {
58 	}
59 
60 	/* does it fit? */
61 	if (strlen(buf) + strlen(name) + strlen(sep) + 1 > len) {
62 		return ENOMEM;
63 	}
64 
65 	strcat(buf, name);
66 	strcat(buf, sep);
67 
68 	return 0;
69 }
70 
71 /*===========================================================================*
72  *          devman_device_add_event                                          *
73  *===========================================================================*/
74 static void
75 devman_device_add_event(struct devman_device* dev)
76 {
77 	struct devman_event * event;
78 	char buf[12]; /* this fits the device ID " 0xXXXXXXXX" */
79 	int res;
80 
81 	event = malloc(sizeof(struct devman_event));
82 
83 	if (event == NULL) {
84 		panic("devman_device_remove_event: out of memory\n");
85 	}
86 
87 	memset(event, 0, sizeof(*event));
88 
89 	strncpy(event->data, ADD_STRING, DEVMAN_STRING_LEN - 1);
90 
91 	res = devman_generate_path(event->data, DEVMAN_STRING_LEN - 11 , dev);
92 
93 	if (res) {
94 		panic("devman_device_add_event: "
95 		    "devman_generate_path failed: (%d)\n", res);
96 	}
97 
98 	snprintf(buf, 12, " 0x%08x", dev->dev_id);
99 	strcat(event->data,buf);
100 
101 	TAILQ_INSERT_HEAD(&event_inode_data.event_queue, event, events);
102 }
103 
104 /*===========================================================================*
105  *          devman_device_remove_event                                       *
106  *===========================================================================*/
107 static void
108 devman_device_remove_event(struct devman_device* dev)
109 {
110 	struct devman_event * event;
111 	char buf[12]; /* this fits the device ID " 0xXXXXXXXX" */
112 	int res;
113 
114 	event = malloc(sizeof(struct devman_event));
115 
116 	if (event == NULL) {
117 		panic("devman_device_remove_event: out of memory\n");
118 	}
119 
120 	memset(event, 0, sizeof(*event));
121 
122 	strncpy(event->data, REMOVE_STRING, DEVMAN_STRING_LEN - 1);
123 
124 	res = devman_generate_path(event->data, DEVMAN_STRING_LEN-11, dev);
125 
126 	if (res) {
127 		panic("devman_device_remove_event: "
128 		    "devman_generate_path failed: (%d)\n", res);
129 	}
130 
131 	snprintf(buf, 12, " 0x%08x", dev->dev_id);
132 	strcat(event->data,buf);
133 
134 
135 	TAILQ_INSERT_HEAD(&event_inode_data.event_queue, event, events);
136 }
137 
138 /*===========================================================================*
139  *          devman_event_read                                                *
140  *===========================================================================*/
141 static int
142 devman_event_read(char **ptr, size_t *len,off_t offset, void *data)
143 {
144 	struct devman_event *ev = NULL;
145 	struct devman_event_inode *n;
146 	static int eof = 0;
147 
148 	if (eof) {
149 		*len=0;
150 		eof = 0;
151 		return 0;
152 	}
153 	n = (struct devman_event_inode *) data;
154 
155 	if (!TAILQ_EMPTY(&n->event_queue)) {
156 		ev = TAILQ_LAST(&n->event_queue, event_head);
157 	}
158 
159 	buf_init(offset, *len);
160 	if (ev != NULL) {
161 		buf_printf("%s", ev->data);
162 		/* read all? */
163 		if (*len + offset >= strlen(ev->data)) {
164 			TAILQ_REMOVE(&n->event_queue, ev, events);
165 			free(ev);
166 			eof = 1;
167 		}
168 	}
169 
170 	*len = buf_get(ptr);
171 
172 	return 0;
173 }
174 
175 /*===========================================================================*
176  *          devman_static_info_read                                          *
177  *===========================================================================*/
178 static int
179 devman_static_info_read(char **ptr, size_t *len, off_t offset, void *data)
180 {
181 	struct devman_static_info_inode *n;
182 
183 	n = (struct devman_static_info_inode *) data;
184 
185 	buf_init(offset, *len);
186 	buf_printf("%s\n", n->data);
187 	*len = buf_get(ptr);
188 	return 0;
189 }
190 
191 /*===========================================================================*
192  *           devman_init_devices                                             *
193  *===========================================================================*/
194 void devman_init_devices()
195 {
196 	event_inode.data   =  &event_inode_data;
197 	event_inode.read_fn =  devman_event_read;
198 
199 	root_dev.dev_id =    0;
200 	root_dev.major  =   -1;
201 	root_dev.owner  =    0;
202 	root_dev.parent = NULL;
203 
204 	root_dev.inode.inode=
205 		add_inode(get_root_inode(), "devices",
206 		    NO_INDEX, &default_dir_stat, 0, &root_dev.inode);
207 
208 	event_inode.inode=
209 		add_inode(get_root_inode(), "events",
210 		    NO_INDEX, &default_file_stat, 0, &event_inode);
211 
212 	TAILQ_INIT(&root_dev.children);
213 	TAILQ_INIT(&root_dev.infos);
214 }
215 
216 
217 /*===========================================================================*
218  *           do_reply                                                        *
219  *===========================================================================*/
220 static void do_reply(message *msg, int res)
221 {
222 	msg->m_type = DEVMAN_REPLY;
223 	msg->DEVMAN_RESULT = res;
224 	ipc_send(msg->m_source, msg);
225 }
226 
227 /*===========================================================================*
228  *           do_add_device                                                   *
229  *===========================================================================*/
230 int do_add_device(message *msg)
231 {
232 	endpoint_t ep = msg->m_source;
233 	int res;
234 	struct devman_device *dev;
235 	struct devman_device *parent;
236 	struct devman_device_info *devinf = NULL;
237 
238 	devinf = malloc(msg->DEVMAN_GRANT_SIZE);
239 
240 	if (devinf == NULL) {
241 		res = ENOMEM;
242 		do_reply(msg, res);
243 		return 0;
244 	}
245 
246 	res = sys_safecopyfrom(ep, msg->DEVMAN_GRANT_ID,
247 	          0, (vir_bytes) devinf, msg->DEVMAN_GRANT_SIZE);
248 
249 	if (res != OK) {
250 		res = EINVAL;
251 		free(devinf);
252 		do_reply(msg, res);
253 		return 0;
254 	}
255 
256 	if ((parent = _find_dev(&root_dev, devinf->parent_dev_id))
257 		 == NULL) {
258 		res = ENODEV;
259 		free(devinf);
260 		do_reply(msg, res);
261 		return 0;
262 	}
263 
264 	dev = devman_dev_add_child(parent, devinf);
265 
266 	if (dev == NULL) {
267 		res = ENODEV;
268 		free(devinf);
269 		do_reply(msg, res);
270 		return 0;
271 	}
272 
273 	dev->state = DEVMAN_DEVICE_UNBOUND;
274 
275 	dev->owner = msg->m_source;
276 
277 	msg->DEVMAN_DEVICE_ID = dev->dev_id;
278 
279 	devman_device_add_event(dev);
280 
281 	do_reply(msg, res);
282 	return 0;
283 }
284 
285 
286 /*===========================================================================*
287  *           _find_dev                                                       *
288  *===========================================================================*/
289 static struct devman_device *
290 _find_dev(struct devman_device *dev, int dev_id)
291 {
292 	struct devman_device *_dev;
293 
294 	if(dev->dev_id == dev_id)
295 		return dev;
296 
297 	TAILQ_FOREACH(_dev, &dev->children, siblings) {
298 
299 		struct devman_device *t = _find_dev(_dev, dev_id);
300 
301 		if (t !=NULL) {
302 			return t;
303 		}
304 	}
305 
306 	return NULL;
307 }
308 
309 /*===========================================================================*
310  *           devman_find_dev                                                 *
311  *===========================================================================*/
312 struct devman_device *devman_find_device(int dev_id)
313 {
314 	return _find_dev(&root_dev, dev_id);
315 }
316 
317 /*===========================================================================*
318  *           devman_dev_add_static_info                                      *
319  *===========================================================================*/
320 static int
321 devman_dev_add_static_info
322 (struct devman_device *dev, char * name, char *data)
323 {
324 	struct devman_inode *inode;
325 	struct devman_static_info_inode *st_inode;
326 
327 
328 	st_inode          = malloc(sizeof(struct devman_static_info_inode));
329 	st_inode->dev     = dev;
330 
331 	strncpy(st_inode->data, data, DEVMAN_STRING_LEN);
332 	/* if string is longer it's truncated */
333 	st_inode->data[DEVMAN_STRING_LEN-1] = 0;
334 
335 	inode          = malloc (sizeof(struct devman_inode));
336 	inode->data    = st_inode;
337 	inode->read_fn = devman_static_info_read;
338 
339 	inode->inode = add_inode(dev->inode.inode, name,
340 			NO_INDEX, &default_file_stat, 0, inode);
341 
342 	/* add info to info_list */
343 	TAILQ_INSERT_HEAD(&dev->infos, inode, inode_list);
344 
345 	return 0;
346 }
347 
348 /*===========================================================================*
349  *           devman_dev_add_child                                            *
350  *===========================================================================*/
351 static struct devman_device*
352 devman_dev_add_child
353 (struct devman_device *parent, struct devman_device_info *devinf)
354 {
355 	int i;
356 	char * buffer = (char *) (devinf);
357 	char tmp_buf[128];
358 	struct devman_device_info_entry *entries;
359 
360 	/* create device */
361 	struct devman_device * dev = malloc(sizeof(struct devman_device));
362 	if (dev == NULL) {
363 		panic("devman_dev_add_child: out of memory\n");
364 	}
365 
366 
367 	if (parent == NULL) {
368 		free(dev);
369 		return NULL;
370 	}
371 
372 	dev->ref_count = 1;
373 
374 	/* set dev_info */
375 	dev->parent   = parent;
376 	dev->info = devinf;
377 
378     dev->dev_id = next_device_id++;
379 
380 	dev->inode.inode =
381 		add_inode(parent->inode.inode, buffer + devinf->name_offset,
382 		    NO_INDEX, &default_dir_stat, 0, &dev->inode);
383 
384 	TAILQ_INIT(&dev->children);
385 	TAILQ_INIT(&dev->infos);
386 
387 	/* create information inodes */
388 	entries = (struct devman_device_info_entry *)
389 		(buffer + sizeof(struct devman_device_info));
390 
391 	for (i = 0; i < devinf->count ; i++) {
392 		devman_dev_add_info(dev, &entries[i], buffer);
393 	}
394 
395 	/* make device ID accessible to user land */
396 	snprintf(tmp_buf, DEVMAN_STRING_LEN, "%d",dev->dev_id);
397 	devman_dev_add_static_info(dev, "devman_id", tmp_buf);
398 
399 	TAILQ_INSERT_HEAD(&parent->children, dev, siblings);
400 
401 	devman_get_device(parent);
402 
403 	/* FUTURE TODO: create links(BUS, etc) */
404 	return dev;
405 }
406 
407 /*===========================================================================*
408  *           devman_dev_add_info                                             *
409  *===========================================================================*/
410 static int
411 devman_dev_add_info
412 (struct devman_device *dev, struct devman_device_info_entry *entry, char *buf)
413 {
414 	switch(entry->type) {
415 
416 	case DEVMAN_DEVINFO_STATIC:
417 			return devman_dev_add_static_info(dev,
418 			    buf + entry->name_offset, buf + entry->data_offset);
419 
420 	case DEVMAN_DEVINFO_DYNAMIC:
421 		/* TODO */
422 		/* fall through */
423 	default:
424 		return -1;
425 	}
426 }
427 
428 /*===========================================================================*
429  *           do_del_device                                                   *
430  *===========================================================================*/
431 int do_del_device(message *msg)
432 {
433 	int dev_id = msg->DEVMAN_DEVICE_ID;
434 
435 	int res=0;
436 
437 	/* only parrent is allowed to add devices */
438 	struct devman_device *dev = _find_dev(&root_dev, dev_id);
439 
440 	if (dev == NULL )  {
441 		printf("devman: no dev with id %d\n",dev_id);
442 		res = ENODEV;
443 	}
444 
445 #if 0
446 	if  (dev->parent->owner != ep) {
447 		res = EPERM;
448 	}
449 #endif
450 
451 	if (!res) {
452 		devman_device_remove_event(dev);
453 		if (dev->state == DEVMAN_DEVICE_BOUND) {
454 			dev->state = DEVMAN_DEVICE_ZOMBIE;
455 		}
456 		devman_put_device(dev);
457 	}
458 
459 	do_reply(msg, res);
460 
461 	return 0;
462 }
463 
464 /*===========================================================================*
465  *           devman_get_device                                               *
466  *===========================================================================*/
467 void devman_get_device(struct devman_device *dev)
468 {
469 	if (dev == NULL || dev == &root_dev) {
470 		return;
471 	}
472 	dev->ref_count++;
473 }
474 
475 /*===========================================================================*
476  *           devman_put_device                                               *
477  *===========================================================================*/
478 void devman_put_device(struct devman_device *dev)
479 {
480 	if (dev == NULL || dev == &root_dev ) {
481 		return;
482 	}
483 	dev->ref_count--;
484 	if (dev->ref_count == 0) {
485 		devman_del_device(dev);
486 	}
487 }
488 
489 /*===========================================================================*
490  *           devman_del_device                                               *
491  *===========================================================================*/
492 static int devman_del_device(struct devman_device *dev)
493 {
494 	/* does device have children -> error */
495 	/* evtl. remove links */
496 
497 	/* free devinfo inodes */
498 	struct devman_inode *inode, *_inode;
499 
500 	TAILQ_FOREACH_SAFE(inode, &dev->infos, inode_list, _inode) {
501 
502 		delete_inode(inode->inode);
503 
504 		TAILQ_REMOVE(&dev->infos, inode, inode_list);
505 
506 		if (inode->data) {
507 			free(inode->data);
508 		}
509 
510 		free(inode);
511 	}
512 
513 	/* free device inode */
514 	delete_inode(dev->inode.inode);
515 
516 	/* remove from parent */
517 	TAILQ_REMOVE(&dev->parent->children, dev, siblings);
518 
519 	devman_put_device(dev->parent);
520 
521 	/* free devinfo */
522 	free(dev->info);
523 
524 	/* free device */
525 	free(dev);
526 	return 0;
527 }
528