* 0xF00 - 0xFFF Scheduling messages
* 0x1000 - 0x10FF Notify messages
* 0x1100 - 0x11FF USB
+ * 0x1200 - 0x12FF Devman
* 0x1300 - 0x13FF TTY Input
*
* Zero and negative values are widely used for OK and error responses.
# define USB_INTERFACES m4_l3
# define USB_RB_INIT_NAME m3_ca1
+/*===========================================================================*
+ * Messages for DeviceManager (s/t like SysFS) *
+ *===========================================================================*/
+
+#define DEVMAN_BASE 0x1200
+
+#define DEVMAN_ADD_DEV (DEVMAN_BASE + 0)
+#define DEVMAN_DEL_DEV (DEVMAN_BASE + 1)
+#define DEVMAN_ADD_BUS (DEVMAN_BASE + 2)
+#define DEVMAN_DEL_BUS (DEVMAN_BASE + 3)
+#define DEVMAN_ADD_DEVFILE (DEVMAN_BASE + 4)
+#define DEVMAN_DEL_DEVFILE (DEVMAN_BASE + 5)
+
+#define DEVMAN_REQUEST (DEVMAN_BASE + 6)
+#define DEVMAN_REPLY (DEVMAN_BASE + 7)
+
+#define DEVMAN_BIND (DEVMAN_BASE + 8)
+#define DEVMAN_UNBIND (DEVMAN_BASE + 9)
+
+# define DEVMAN_GRANT_ID m4_l1
+# define DEVMAN_GRANT_SIZE m4_l2
+
+# define DEVMAN_ENDPOINT m4_l3
+# define DEVMAN_DEVICE_ID m4_l2
+# define DEVMAN_RESULT m4_l1
+
/*===========================================================================*
* TTY INPUT INJECTION *
*===========================================================================*/
minix/vfsif.h minix/vtreefs.h \
minix/compiler-ack.h minix/sha2.h minix/sha1.h minix/md5.h \
minix/audio_fw.h minix/hash.h minix/input.h \
- minix/usb.h minix/usb_ch9.h
+ minix/devman.h minix/usb.h minix/usb_ch9.h
INCS+= net/hton.h net/if.h net/ioctl.h net/netlib.h
INCS+= netinet/if_ether.h netinet/in.h netinet/tcp.h
--- /dev/null
+#ifndef MINIX_LIBDEVMAN_H
+#define MINIX_LIBDEVMAN_H
+#include <minix/com.h>
+#include <minix/ipc.h>
+#include <minix/usb_ch9.h>
+
+/* used for serializing */
+struct devman_device_info {
+ int count;
+ int parent_dev_id;
+ unsigned name_offset;
+ unsigned subsystem_offset;
+};
+
+struct devman_device_info_entry {
+ unsigned type;
+ unsigned name_offset;
+ unsigned data_offset;
+ unsigned req_nr;
+};
+
+#ifndef DEVMAN_SERVER
+struct devman_usb_bind_cb_data {
+ int dev_id;
+ int interface;
+};
+
+struct devman_usb_interface {
+ struct devman_dev *dev;
+ struct devman_usb_dev *usb_dev;
+ usb_interface_descriptor_t *desc;
+ /* used by the lib */
+ struct devman_usb_bind_cb_data cb_data;
+};
+
+struct devman_usb_dev {
+ struct devman_dev *dev;
+ int dev_id; /* The ID identifying the device
+ on server side */
+ usb_device_descriptor_t *desc;
+
+ int configuration; /* the configuration used for this
+ device */
+
+ char *manufacturer;
+ char *product;
+ char *serial;
+
+ int intf_count; /* the number of interfaces in the current
+ configuration */
+
+ struct devman_usb_interface interfaces[32];
+ /* used by the lib */
+ struct devman_usb_bind_cb_data cb_data;
+};
+
+typedef int (*devman_usb_bind_cb_t)(struct devman_usb_bind_cb_data *data, endpoint_t ep);
+
+_PROTOTYPE( int devman_add_device, (struct devman_dev *dev));
+_PROTOTYPE( int devman_del_device, (struct devman_dev *dev));
+_PROTOTYPE( int devman_init, (void));
+_PROTOTYPE( struct devman_usb_dev* devman_usb_device_new, (int dev_id));
+_PROTOTYPE( int devman_usb_device_add, (struct devman_usb_dev *dev));
+_PROTOTYPE( int devman_usb_device_remove, (struct devman_usb_dev *dev));
+_PROTOTYPE( void devman_usb_device_delete, (struct devman_usb_dev *udev));
+_PROTOTYPE( int devman_handle_msg, (message *m));
+_PROTOTYPE( int devman_usb_init,(devman_usb_bind_cb_t bind_cb,
+ devman_usb_bind_cb_t unbind_cb));
+
+#endif
+
+#endif
.include <bsd.own.mk>
SUBDIR= ds ext2 hgfs inet init ipc is iso9660fs \
- mfs pfs pm procfs rs sched vfs vm
+ mfs pfs pm procfs rs sched vfs vm devman
IMAGE_SUBDIR= ds init mfs pfs pm rs sched vfs vm
--- /dev/null
+PROG = devman
+
+SRCS = main.c device.c buf.c bind.c
+
+
+DPADD+= ${LIBSYS}
+LDADD = -lvtreefs -lsys
+BINDIR?= /sbin
+INSTALLFLAGS+= -S 128k
+
+MAN=
+.include <bsd.prog.mk>
+CFLAGS += -Wall
--- /dev/null
+#include "devman.h"
+#include "proto.h"
+
+/*****************************************************************************
+ * do_bind_device *
+ ****************************************************************************/
+PUBLIC int do_bind_device(message *m)
+{
+ struct devman_device *dev;
+ int res;
+ endpoint_t src = m->m_source;
+
+ /* check if msg comes from RS */
+ if (src != RS_PROC_NR) {
+ m->DEVMAN_RESULT = EPERM;
+ printf("[W] could bind message from somebody else than RS\n");
+
+ return 0;
+ } else {
+ /* get the device */
+ dev = devman_find_device(m->DEVMAN_DEVICE_ID);
+ /* bind device at device provider*/
+ if (dev != NULL) {
+ m->m_type = DEVMAN_BIND;
+ /* ...device ID and endpoint is still set */
+
+#ifdef DEBUG
+ printf("devman: bind call to %d for dev %d\n",
+ dev->owner, m->DEVMAN_DEVICE_ID);
+#endif
+
+ res = sendrec(dev->owner, m);
+ if (res != OK) {
+ printf("[W] devman.do_bind_device(): could not send "
+ "message to device owner (%d)\n", res);
+ m->DEVMAN_RESULT= res;
+ } else if (m->DEVMAN_RESULT != OK) {
+ printf("[W] devman.do_bind_device(): driver could"
+ " not bind device (%d)\n", m->DEVMAN_RESULT);
+ } else {
+ dev->state = DEVMAN_DEVICE_BOUND;
+ devman_get_device(dev);
+ }
+ } else {
+ m->DEVMAN_RESULT = ENODEV;
+ }
+ m->m_type = DEVMAN_REPLY;
+ send(RS_PROC_NR, m);
+ }
+ return 0;
+}
+
+/*****************************************************************************
+ * do_unbind_device *
+ ****************************************************************************/
+PUBLIC int do_unbind_device(message *m)
+{
+ struct devman_device *dev;
+ int res;
+ endpoint_t src = m->m_source;
+
+ /* check if msg comes from RS */
+ if (src != RS_PROC_NR) {
+ m->DEVMAN_RESULT = EPERM;
+ printf("[W] devman.do_unbind_device(): unbind message from somebody"
+ "else than RS (%d)\n", src);
+ return 0;
+ } else {
+ /* get the device */
+ dev = devman_find_device(m->DEVMAN_DEVICE_ID);
+ /* bind device at device provider*/
+ if (dev != NULL) {
+
+ m->m_type = DEVMAN_UNBIND;
+ /* ...device ID and endpoint is still set */
+#ifdef DEBUG
+ printf("devman: unbind call to %d for dev %d\n",
+ dev->owner, m->DEVMAN_DEVICE_ID);
+#endif
+ res = sendrec(dev->owner, m);
+ if (res != OK) {
+ printf("[W] devman.do_unbind_device(): could not send "
+ "message to device owner (%d)\n", res);
+ m->DEVMAN_RESULT= res;
+ } else if (m->DEVMAN_RESULT != OK && m->DEVMAN_RESULT != 19) {
+ /* device drive deleted device already? */
+ printf("[W] devman.do_unbind_device(): driver could"
+ " not unbind device (%d)\n", m->DEVMAN_RESULT);
+ } else {
+ if (dev->state != DEVMAN_DEVICE_ZOMBIE) {
+ dev->state = DEVMAN_DEVICE_UNBOUND;
+ }
+ devman_put_device(dev);
+ m->DEVMAN_RESULT = OK;
+ }
+ } else {
+ /* this might be the case, but perhaps its better to keep
+ the device in the db as long a driver is bound to it*/
+ m->DEVMAN_RESULT = ENODEV;
+ }
+ m->m_type = DEVMAN_REPLY;
+ send(RS_PROC_NR, m);
+ }
+ return 0;
+}
--- /dev/null
+/* buf.c - by Alen Stojanov and David van Moolenbroek, taken from procfs */
+#define _POSIX_SOURCE 1 /* tell headers to include POSIX stuff */
+#define _MINIX 1 /* tell headers to include MINIX stuff */
+#define _SYSTEM 1 /* tell headers that this is the kernel */
+#define DEVMAN_SERVER 1
+
+#include <minix/config.h>
+#include <errno.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <lib.h>
+#include <timers.h>
+
+#include <minix/callnr.h>
+#include <minix/type.h>
+#include <minix/const.h>
+#include <minix/com.h>
+#include <minix/syslib.h>
+#include <minix/sysutil.h>
+#include <minix/vfsif.h>
+#include <minix/endpoint.h>
+#include <minix/sysinfo.h>
+#include <minix/u64.h>
+#include <minix/sysinfo.h>
+#include <minix/type.h>
+#include <minix/ipc.h>
+
+#include <sys/time.h>
+#include <sys/times.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+
+#include <minix/vtreefs.h>
+
+#include <minix/devman.h>
+
+
+#include <stdarg.h>
+#include <assert.h>
+#include <string.h>
+#define BUF_SIZE 4096
+
+PRIVATE char buf[BUF_SIZE + 1];
+PRIVATE size_t off, left, used;
+PRIVATE off_t skip;
+
+#define MIN(x,y) (x<y?x:y)
+
+/*===========================================================================*
+ * buf_init *
+ *===========================================================================*/
+PUBLIC void buf_init(off_t start, size_t len)
+{
+ /* Initialize the buffer for fresh use. The first 'start' bytes of the
+ * produced output are to be skipped. After that, up to a total of
+ * 'len' bytes are requested.
+ */
+
+ skip = start;
+ left = MIN(len, BUF_SIZE);
+ off = 0;
+ used = 0;
+}
+
+/*===========================================================================*
+ * buf_printf *
+ *===========================================================================*/
+PUBLIC void buf_printf(char *fmt, ...)
+{
+ /* Add formatted text to the end of the buffer.
+ */
+ va_list args;
+ ssize_t len, max;
+
+ if (left == 0)
+ return;
+
+ /* There is no way to estimate how much space the result will take, so
+ * we need to produce the string even when skipping part of the start.
+ * If part of the result is to be skipped, do not memcpy; instead, save
+ * the offset of where the result starts within the buffer.
+ *
+ * The null terminating character is not part of the result, so room
+ * must be given for it to be stored after completely filling up the
+ * requested part of the buffer.
+ */
+ max = MIN(skip + left, BUF_SIZE);
+
+ va_start(args, fmt);
+ len = vsnprintf(&buf[off + used], max + 1, fmt, args);
+ va_end(args);
+
+ if (skip > 0) {
+ assert(off == 0);
+ assert(used == 0);
+
+ if (skip >= len) {
+ skip -= len;
+
+ return;
+ }
+
+ off = skip;
+ if (left > BUF_SIZE - off)
+ left = BUF_SIZE - off;
+ len -= off;
+ skip = 0;
+ }
+
+ assert(skip == 0);
+ assert(len >= 0);
+ assert((long) left >= 0);
+
+ if (len > (ssize_t) left)
+ len = left;
+
+ used += len;
+ left -= len;
+}
+
+/*===========================================================================*
+ * buf_append *
+ *===========================================================================*/
+PUBLIC void buf_append(char *data, size_t len)
+{
+ /* Add arbitrary data to the end of the buffer.
+ */
+
+ if (left == 0)
+ return;
+
+ if (skip > 0) {
+ if (skip >= (ssize_t) len) {
+ skip -= len;
+
+ return;
+ }
+
+ data += skip;
+ len -= skip;
+ skip = 0;
+ }
+
+ if (len > left)
+ len = left;
+
+ memcpy(&buf[off + used], data, len);
+
+ used += len;
+ left -= len;
+}
+
+/*===========================================================================*
+ * buf_get *
+ *===========================================================================*/
+PUBLIC size_t buf_get(char **ptr)
+{
+ /* Return the buffer's starting address and the length of the used
+ * part, not counting the trailing null character for the latter.
+ */
+
+ *ptr = &buf[off];
+
+ return used;
+}
--- /dev/null
+#include "devman.h"
+#include "proto.h"
+
+
+FORWARD _PROTOTYPE( struct devman_device*devman_dev_add_child,
+ (struct devman_device *parent,
+ struct devman_device_info *devinf) );
+FORWARD _PROTOTYPE( struct devman_device *_find_dev,
+ (struct devman_device *dev, int dev_id) );
+FORWARD _PROTOTYPE( int devman_dev_add_info,
+ (struct devman_device *dev,
+ struct devman_device_info_entry *entry,
+ char *buf) );
+FORWARD _PROTOTYPE( int devman_event_read,
+ (char **ptr, size_t *len,off_t offset, void *data) );
+
+FORWARD _PROTOTYPE( int devman_del_device, (struct devman_device *dev) );
+
+PRIVATE int next_device_id = 1;
+
+PRIVATE struct inode_stat default_dir_stat = {
+ /* .mode = */ S_IFDIR | S_IRUSR | S_IRGRP | S_IROTH,
+ /* .uid = */ 0,
+ /* .gid = */ 0,
+ /* .size = */ 0,
+ /* .dev = */ NO_DEV,
+};
+
+PRIVATE struct inode_stat default_file_stat = {
+ /* .mode = */ S_IFREG | S_IRUSR | S_IRGRP | S_IROTH,
+ /* .uid = */ 0,
+ /* .gid = */ 0,
+ /* .size = */ 0x1000,
+ /* .dev = */ NO_DEV,
+};
+
+
+PRIVATE struct devman_device root_dev;
+PRIVATE struct devman_event_inode event_inode_data = {
+ TAILQ_HEAD_INITIALIZER(event_inode_data.event_queue),
+};
+PRIVATE struct devman_inode event_inode;
+
+/*===========================================================================*
+ * devman_generate_path *
+ *===========================================================================*/
+PRIVATE int
+devman_generate_path(char* buf, int len, struct devman_device *dev)
+{
+ int res =0;
+ const char * name = ".";
+ const char * sep = "/";
+
+ if (dev != NULL) {
+ res = devman_generate_path(buf, len, dev->parent);
+ if (res != 0) {
+ return res;
+ }
+ name = get_inode_name(dev->inode.inode);
+ } else {
+ }
+
+ /* does it fit? */
+ if (strlen(buf) + strlen(name) + strlen(sep) + 1 > len) {
+ return ENOMEM;
+ }
+
+ strcat(buf, name);
+ strcat(buf, sep);
+
+ return 0;
+}
+
+/*===========================================================================*
+ * devman_device_add_event *
+ *===========================================================================*/
+PRIVATE void
+devman_device_add_event(struct devman_device* dev)
+{
+ struct devman_event * event;
+ char buf[12]; /* this fits the device ID " 0xXXXXXXXX" */
+ int res;
+
+ event = malloc(sizeof(struct devman_event));
+ memset(event, 0, sizeof(event));
+
+ if (event == NULL) {
+ panic("devman_device_remove_event: out of memory\n");
+ }
+
+ strcat(event->data, ADD_STRING);
+
+ res = devman_generate_path(event->data, DEVMAN_STRING_LEN - 11 , dev);
+
+ if (res) {
+ panic("devman_device_add_event: "
+ "devman_generate_path failed: (%d)\n", res);
+ }
+
+ snprintf(buf, 12, " 0x%08x", dev->dev_id);
+ strcat(event->data,buf);
+
+ TAILQ_INSERT_HEAD(&event_inode_data.event_queue, event, events);
+}
+
+/*===========================================================================*
+ * devman_device_remove_event *
+ *===========================================================================*/
+PRIVATE void
+devman_device_remove_event(struct devman_device* dev)
+{
+ struct devman_event * event;
+ char buf[12]; /* this fits the device ID " 0xXXXXXXXX" */
+ int res;
+
+ event = malloc(sizeof(struct devman_event));
+ memset(event, 0, sizeof(event));
+
+ if (event == NULL) {
+ panic("devman_device_remove_event: out of memory\n");
+ }
+
+ strcat(event->data, REMOVE_STRING);
+
+ res = devman_generate_path(event->data, DEVMAN_STRING_LEN-11, dev);
+
+ if (res) {
+ panic("devman_device_remove_event: "
+ "devman_generate_path failed: (%d)\n", res);
+ }
+
+ snprintf(buf, 12, " 0x%08x", dev->dev_id);
+ strcat(event->data,buf);
+
+
+ TAILQ_INSERT_HEAD(&event_inode_data.event_queue, event, events);
+}
+
+/*===========================================================================*
+ * devman_event_read *
+ *===========================================================================*/
+PRIVATE int
+devman_event_read(char **ptr, size_t *len,off_t offset, void *data)
+{
+ struct devman_event *ev = NULL;
+ struct devman_event_inode *n;
+ static int eof = 0;
+
+ if (eof) {
+ *len=0;
+ eof = 0;
+ return 0;
+ }
+ n = (struct devman_event_inode *) data;
+
+ if (!TAILQ_EMPTY(&n->event_queue)) {
+ ev = TAILQ_LAST(&n->event_queue, event_head);
+ }
+
+ buf_init(offset, *len);
+ if (ev != NULL) {
+ buf_printf("%s", ev->data);
+ /* read all? */
+ if (*len + offset >= strlen(ev->data)) {
+ TAILQ_REMOVE(&n->event_queue, ev, events);
+ free(ev);
+ eof = 1;
+ }
+ }
+
+ *len = buf_get(ptr);
+
+ return 0;
+}
+
+/*===========================================================================*
+ * devman_static_info_read *
+ *===========================================================================*/
+PRIVATE int
+devman_static_info_read(char **ptr, size_t *len, off_t offset, void *data)
+{
+ struct devman_static_info_inode *n;
+
+ n = (struct devman_static_info_inode *) data;
+
+ buf_init(offset, *len);
+ buf_printf("%s\n", n->data);
+ *len = buf_get(ptr);
+ return 0;
+}
+
+/*===========================================================================*
+ * devman_init_devices *
+ *===========================================================================*/
+PUBLIC void devman_init_devices()
+{
+ event_inode.data = &event_inode_data;
+ event_inode.read_fn = devman_event_read;
+
+ root_dev.dev_id = 0;
+ root_dev.major = -1;
+ root_dev.owner = 0;
+ root_dev.parent = NULL;
+
+ root_dev.inode.inode=
+ add_inode(get_root_inode(), "devices",
+ NO_INDEX, &default_dir_stat, 0, &root_dev.inode);
+
+ event_inode.inode=
+ add_inode(get_root_inode(), "events",
+ NO_INDEX, &default_file_stat, 0, &event_inode);
+
+ TAILQ_INIT(&root_dev.children);
+ TAILQ_INIT(&root_dev.infos);
+}
+
+
+/*===========================================================================*
+ * do_reply *
+ *===========================================================================*/
+PRIVATE void do_reply(message *msg, int res)
+{
+ msg->m_type = DEVMAN_REPLY;
+ msg->DEVMAN_RESULT = res;
+ send(msg->m_source, msg);
+}
+
+/*===========================================================================*
+ * do_add_device *
+ *===========================================================================*/
+PUBLIC int do_add_device(message *msg)
+{
+ endpoint_t ep = msg->m_source;
+ int res;
+ struct devman_device *dev;
+ struct devman_device *parent;
+ struct devman_device_info *devinf = NULL;
+
+ devinf = malloc(msg->DEVMAN_GRANT_SIZE);
+
+ if (devinf == NULL) {
+ res = ENOMEM;
+ do_reply(msg, res);
+ return 0;
+ }
+
+ res = sys_safecopyfrom(ep, msg->DEVMAN_GRANT_ID,
+ 0, (vir_bytes) devinf, msg->DEVMAN_GRANT_SIZE, D);
+
+ if (res != OK) {
+ res = EINVAL;
+ free(devinf);
+ do_reply(msg, res);
+ return 0;
+ }
+
+ if ((parent = _find_dev(&root_dev, devinf->parent_dev_id))
+ == NULL) {
+ res = ENODEV;
+ free(devinf);
+ do_reply(msg, res);
+ return 0;
+ }
+
+ dev = devman_dev_add_child(parent, devinf);
+ dev->state = DEVMAN_DEVICE_UNBOUND;
+
+ if (dev == NULL) {
+ res = ENODEV;
+ free(devinf);
+ do_reply(msg, res);
+ return 0;
+ }
+
+ dev->owner = msg->m_source;
+
+ msg->DEVMAN_DEVICE_ID = dev->dev_id;
+
+ devman_device_add_event(dev);
+
+ do_reply(msg, res);
+ return 0;
+}
+
+
+/*===========================================================================*
+ * _find_dev *
+ *===========================================================================*/
+PRIVATE struct devman_device *
+_find_dev(struct devman_device *dev, int dev_id)
+{
+ struct devman_device *_dev;
+
+ if(dev->dev_id == dev_id)
+ return dev;
+
+ TAILQ_FOREACH(_dev, &dev->children, siblings) {
+
+ struct devman_device *t = _find_dev(_dev, dev_id);
+
+ if (t !=NULL) {
+ return t;
+ }
+ }
+
+ return NULL;
+}
+
+/*===========================================================================*
+ * devman_find_dev *
+ *===========================================================================*/
+PUBLIC struct devman_device *devman_find_device(int dev_id)
+{
+ return _find_dev(&root_dev, dev_id);
+}
+
+/*===========================================================================*
+ * devman_dev_add_static_info *
+ *===========================================================================*/
+PRIVATE int
+devman_dev_add_static_info
+(struct devman_device *dev, char * name, char *data)
+{
+ struct devman_inode *inode;
+ struct devman_static_info_inode *st_inode;
+
+
+ st_inode = malloc(sizeof(struct devman_static_info_inode));
+ st_inode->dev = dev;
+
+ strncpy(st_inode->data, data, DEVMAN_STRING_LEN);
+ /* if string is longer it's truncated */
+ st_inode->data[DEVMAN_STRING_LEN-1] = 0;
+
+ inode = malloc (sizeof(struct devman_inode));
+ inode->data = st_inode;
+ inode->read_fn = devman_static_info_read;
+
+ inode->inode = add_inode(dev->inode.inode, name,
+ NO_INDEX, &default_file_stat, 0, inode);
+
+ /* add info to info_list */
+ TAILQ_INSERT_HEAD(&dev->infos, inode, inode_list);
+
+ return 0;
+}
+
+/*===========================================================================*
+ * devman_dev_add_child *
+ *===========================================================================*/
+PRIVATE struct devman_device*
+devman_dev_add_child
+(struct devman_device *parent, struct devman_device_info *devinf)
+{
+ int i;
+ char * buffer = (char *) (devinf);
+ char tmp_buf[128];
+ struct devman_device_info_entry *entries;
+ /* create device */
+ struct devman_device * dev = malloc(sizeof(struct devman_device));
+
+ if (parent == NULL) {
+ return NULL;
+ }
+
+ dev->ref_count = 1;
+
+ /* set dev_info */
+ dev->parent = parent;
+ dev->info = devinf;
+
+ dev->dev_id = next_device_id++;
+
+ dev->inode.inode =
+ add_inode(parent->inode.inode, buffer + devinf->name_offset,
+ NO_INDEX, &default_dir_stat, 0, &dev->inode);
+
+ TAILQ_INIT(&dev->children);
+ TAILQ_INIT(&dev->infos);
+
+ /* create information inodes */
+ entries = (struct devman_device_info_entry *)
+ (buffer + sizeof(struct devman_device_info));
+
+ for (i = 0; i < devinf->count ; i++) {
+ devman_dev_add_info(dev, &entries[i], buffer);
+ }
+
+ /* make device ID accessible to user land */
+ snprintf(tmp_buf, DEVMAN_STRING_LEN, "%d",dev->dev_id);
+ devman_dev_add_static_info(dev, "devman_id", tmp_buf);
+
+ TAILQ_INSERT_HEAD(&parent->children, dev, siblings);
+
+ devman_get_device(parent);
+
+ /* FUTURE TODO: create links(BUS, etc) */
+ return dev;
+}
+
+/*===========================================================================*
+ * devman_dev_add_info *
+ *===========================================================================*/
+PRIVATE int
+devman_dev_add_info
+(struct devman_device *dev, struct devman_device_info_entry *entry, char *buf)
+{
+ switch(entry->type) {
+
+ case DEVMAN_DEVINFO_STATIC:
+ return devman_dev_add_static_info(dev,
+ buf + entry->name_offset, buf + entry->data_offset);
+
+ case DEVMAN_DEVINFO_DYNAMIC:
+ /* TODO */
+ /* fall through */
+ default:
+ return -1;
+ }
+}
+
+/*===========================================================================*
+ * do_del_device *
+ *===========================================================================*/
+PUBLIC int do_del_device(message *msg)
+{
+ int dev_id = msg->DEVMAN_DEVICE_ID;
+
+ int res=0;
+
+ /* only parrent is allowed to add devices */
+ struct devman_device *dev = _find_dev(&root_dev, dev_id);
+
+ if (dev == NULL ) {
+ printf("devman: no dev with id %d\n",dev_id);
+ res = ENODEV;
+ }
+
+#if 0
+ if (dev->parent->owner != ep) {
+ res = EPERM;
+ }
+#endif
+
+ if (!res) {
+ devman_device_remove_event(dev);
+ if (dev->state == DEVMAN_DEVICE_BOUND) {
+ dev->state = DEVMAN_DEVICE_ZOMBIE;
+ }
+ devman_put_device(dev);
+ }
+
+ do_reply(msg, res);
+
+ return 0;
+}
+
+/*===========================================================================*
+ * devman_get_device *
+ *===========================================================================*/
+PUBLIC void devman_get_device(struct devman_device *dev)
+{
+ if (dev == NULL || dev == &root_dev) {
+ return;
+ }
+ dev->ref_count++;
+}
+
+/*===========================================================================*
+ * devman_put_device *
+ *===========================================================================*/
+PUBLIC void devman_put_device(struct devman_device *dev)
+{
+ if (dev == NULL || dev == &root_dev ) {
+ return;
+ }
+ dev->ref_count--;
+ if (dev->ref_count == 0) {
+ devman_del_device(dev);
+ }
+}
+
+/*===========================================================================*
+ * devman_del_device *
+ *===========================================================================*/
+PRIVATE int devman_del_device(struct devman_device *dev)
+{
+ /* does device have children -> error */
+ /* evtl. remove links */
+
+ /* free devinfo inodes */
+ struct devman_inode *inode, *_inode;
+
+ TAILQ_FOREACH_SAFE(inode, &dev->infos, inode_list, _inode) {
+
+ delete_inode(inode->inode);
+
+ TAILQ_REMOVE(&dev->infos, inode, inode_list);
+
+ if (inode->data) {
+ free(inode->data);
+ }
+
+ free(inode);
+ }
+
+ /* free device inode */
+ delete_inode(dev->inode.inode);
+
+ /* remove from parent */
+ TAILQ_REMOVE(&dev->parent->children, dev, siblings);
+
+ devman_put_device(dev->parent);
+
+ /* free devinfo */
+ free(dev->info);
+
+ /* free device */
+ free(dev);
+ return 0;
+}
--- /dev/null
+#ifndef DEVMAN_DEVINFO_H
+#define DEVMAN_DEVINFO_H 1
+
+
+struct devman_dev {
+ int dev_id;
+ int parent_dev_id;
+ char *name;
+ char *subsys;
+ void *data;
+ TAILQ_HEAD(static_attribute_head, devman_static_attribute) attrs;
+};
+
+struct devman_static_attribute {
+ char *name;
+ char *data;
+ TAILQ_ENTRY(devman_static_attribute) list;
+};
+
+/* used for serializing */
+struct devman_device_info {
+ int count;
+ int parent_dev_id;
+ unsigned name_offset;
+ unsigned subsystem_offset;
+};
+
+struct devman_device_info_entry {
+ unsigned type;
+ unsigned name_offset;
+ unsigned data_offset;
+ unsigned req_nr;
+};
+
+#endif
--- /dev/null
+#ifndef _SERVERS_DEVMAN_DEVMAN_H
+#define _SERVERS_DEVMAN_DEVMAN_H
+#define _POSIX_SOURCE 1 /* tell headers to include POSIX stuff */
+#define _MINIX 1 /* tell headers to include MINIX stuff */
+#define _SYSTEM 1 /* tell headers that this is the kernel */
+#define DEVMAN_SERVER 1
+
+#include <minix/config.h>
+#include <errno.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <lib.h>
+#include <timers.h>
+
+#include <minix/callnr.h>
+#include <minix/type.h>
+#include <minix/const.h>
+#include <minix/com.h>
+#include <minix/syslib.h>
+#include <minix/sysutil.h>
+#include <minix/vfsif.h>
+#include <minix/endpoint.h>
+#include <minix/sysinfo.h>
+#include <minix/u64.h>
+#include <minix/sysinfo.h>
+#include <minix/type.h>
+#include <minix/ipc.h>
+
+#include <sys/time.h>
+#include <sys/times.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+
+#include <minix/vtreefs.h>
+
+#include <minix/devman.h>
+#include <sys/queue.h>
+
+#define DEVMAN_DEFAULT_MODE (S_IRUSR | S_IRGRP | S_IROTH)
+#define DEVMAN_STRING_LEN 128
+
+#define ADD_STRING "ADD "
+#define REMOVE_STRING "REMOVE "
+
+enum devman_inode_type {
+ DEVMAN_DEVINFO_STATIC,
+ DEVMAN_DEVINFO_DYNAMIC,
+ DEVMAN_DEVICE
+};
+
+typedef int (*devman_read_fn)
+ (char **ptr, size_t *len, off_t offset, void *data);
+
+struct devman_device_file {
+ int minor;
+ int type;
+};
+
+struct devman_static_info_inode {
+ struct devman_device *dev;
+ char data[DEVMAN_STRING_LEN];
+};
+
+struct devman_event {
+ char data[DEVMAN_STRING_LEN];
+ TAILQ_ENTRY(devman_event) events;
+};
+
+struct devman_event_inode {
+ TAILQ_HEAD(event_head, devman_event) event_queue;
+};
+
+struct devman_inode {
+ struct inode *inode;
+ devman_read_fn read_fn;
+ void *data;
+ TAILQ_ENTRY(devman_inode) inode_list;
+};
+
+struct devman_device {
+ int dev_id;
+ char * name;
+
+ int ref_count;
+
+ int major;
+#define DEVMAN_DEVICE_ZOMBIE 2
+#define DEVMAN_DEVICE_BOUND 1
+#define DEVMAN_DEVICE_UNBOUND 0
+ int state;
+
+ endpoint_t owner;
+
+ struct devman_inode inode;
+ struct devman_device *parent;
+
+ /* the serialized information on this device */
+ struct devman_device_info *info;
+
+ TAILQ_ENTRY(devman_device) siblings;
+
+ /* devices attached to the this device */
+ TAILQ_HEAD(children_head, devman_device) children;
+ TAILQ_HEAD(info_head, devman_inode) infos;
+};
+#endif
--- /dev/null
+#define _POSIX_SOURCE 1 /* tell headers to include POSIX stuff */
+#define _MINIX 1 /* tell headers to include MINIX stuff */
+#define _SYSTEM 1 /* tell headers that this is the kernel */
+
+#include <minix/config.h>
+#include <errno.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <lib.h>
+#include <timers.h>
+
+#include <minix/callnr.h>
+#include <minix/type.h>
+#include <minix/const.h>
+#include <minix/com.h>
+#include <minix/syslib.h>
+#include <minix/sysutil.h>
+#include <minix/vfsif.h>
+#include <minix/endpoint.h>
+#include <minix/sysinfo.h>
+#include <minix/u64.h>
+#include <minix/sysinfo.h>
+#include <minix/type.h>
+#include <minix/ipc.h>
+
+#include <sys/time.h>
+#include <sys/times.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+
+
+#include <minix/vtreefs.h>
+#include "devman.h"
+#include "proto.h"
+
+PRIVATE void init_hook(void) {
+ static int first = 1;
+
+ if (first) {
+ devman_init_devices();
+ first = 0;
+ }
+}
+
+
+PRIVATE int message_hook (message *m)
+{
+ switch (m->m_type) {
+ case DEVMAN_ADD_DEV:
+ return do_add_device(m);
+ case DEVMAN_DEL_DEV:
+ return do_del_device(m);
+ case DEVMAN_BIND:
+ return do_bind_device(m);
+ case DEVMAN_UNBIND:
+ return do_unbind_device(m);
+ default: return -1;
+ }
+}
+
+PRIVATE int
+read_hook
+(struct inode *inode, off_t offset, char **ptr, size_t *len, cbdata_t cbdata)
+{
+ struct devman_inode *d_inode = (struct devman_inode *) cbdata;
+
+ return d_inode->read_fn(ptr, len, offset, d_inode->data);
+}
+
+
+PUBLIC int main (int argc, char* argv[])
+{
+
+ struct fs_hooks hooks;
+ struct inode_stat root_stat;
+
+ /* fill in the hooks */
+ hooks.init_hook = &init_hook;
+ hooks.lookup_hook = NULL; /* use the default behavior of lookup */
+ hooks.getdents_hook = NULL; /* use the default behavior of getdents */
+ hooks.read_hook = read_hook; /* read hook will never be called */
+ hooks.rdlink_hook = NULL; /* there are no symbolic links in devfs */
+ hooks.message_hook = message_hook; /* handle the ds_update call */
+
+ root_stat.mode = S_IFDIR | S_IRUSR | S_IRGRP | S_IROTH;
+ root_stat.uid = 0;
+ root_stat.gid = 0;
+ root_stat.size = 0;
+ root_stat.dev = NO_DEV;
+
+ /* limit the number of indexed entries */
+ start_vtreefs(&hooks, 1024 , &root_stat, 0);
+ return 0;
+}
+
--- /dev/null
+#ifndef _DEVMAN_PROTO_H
+#define _DEVMAN_PROTO_H
+
+/* buf.c */
+_PROTOTYPE( void buf_init, (off_t start, size_t len) );
+_PROTOTYPE( void buf_printf, (char *fmt, ...) );
+_PROTOTYPE( void buf_append, (char *data, size_t len) );
+_PROTOTYPE( size_t buf_get, (char **ptr) );
+
+/* message handlers */
+_PROTOTYPE(int do_add_device, (message *m));
+_PROTOTYPE(int do_del_device, (message *m));
+_PROTOTYPE(int do_bind_device, (message *m));
+_PROTOTYPE(int do_unbind_device, (message *m));
+
+/* local helper functions */
+_PROTOTYPE(void devman_init_devices, ());
+_PROTOTYPE(struct devman_device* devman_find_device,(int devid));
+_PROTOTYPE(void devman_get_device, (struct devman_device *dev));
+_PROTOTYPE(void devman_put_device, (struct devman_device *dev));
+
+#endif /* _DEVMAN_PROTO_H */
+