]> Zhao Yanbai Git Server - minix.git/commitdiff
Added Device Manager (USB hotplug support)
authorDirk Vogt <dirk@minix3.org>
Wed, 23 Feb 2011 13:48:03 +0000 (13:48 +0000)
committerDirk Vogt <dirk@minix3.org>
Wed, 23 Feb 2011 13:48:03 +0000 (13:48 +0000)
12 files changed:
common/include/minix/com.h
include/Makefile
include/minix/devman.h [new file with mode: 0644]
servers/Makefile
servers/devman/Makefile [new file with mode: 0644]
servers/devman/bind.c [new file with mode: 0644]
servers/devman/buf.c [new file with mode: 0755]
servers/devman/device.c [new file with mode: 0644]
servers/devman/devinfo.h [new file with mode: 0644]
servers/devman/devman.h [new file with mode: 0644]
servers/devman/main.c [new file with mode: 0644]
servers/devman/proto.h [new file with mode: 0644]

index 9094b3bbddaf32d710fee6e421758f8f3364f4e4..ffe9bdaeb2b5bc553d5cf66cfdb0f08b8083a3bd 100644 (file)
@@ -23,6 +23,7 @@
  *    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                                          *
  *===========================================================================*/
index fdc087331d23b810788ecfbd20e8d9a2910af6ed..708955aa1084eeae7111aae71cf663dd343bd3b9 100644 (file)
@@ -27,7 +27,7 @@ INCS+=        minix/a.out.h minix/cdrom.h minix/cpufeature.h \
        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
diff --git a/include/minix/devman.h b/include/minix/devman.h
new file mode 100644 (file)
index 0000000..48ccb7f
--- /dev/null
@@ -0,0 +1,72 @@
+#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
index 72fac3b8ae3c281cefe7cba63e8b467577e5ef2c..25aaf6493748b995b6819e493c40b86f03d4d48c 100644 (file)
@@ -4,7 +4,7 @@
 .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
 
diff --git a/servers/devman/Makefile b/servers/devman/Makefile
new file mode 100644 (file)
index 0000000..1a6840e
--- /dev/null
@@ -0,0 +1,13 @@
+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
diff --git a/servers/devman/bind.c b/servers/devman/bind.c
new file mode 100644 (file)
index 0000000..c81baf8
--- /dev/null
@@ -0,0 +1,105 @@
+#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;
+}
diff --git a/servers/devman/buf.c b/servers/devman/buf.c
new file mode 100755 (executable)
index 0000000..c6f1f70
--- /dev/null
@@ -0,0 +1,167 @@
+/* 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;
+}
diff --git a/servers/devman/device.c b/servers/devman/device.c
new file mode 100644 (file)
index 0000000..fa502c8
--- /dev/null
@@ -0,0 +1,521 @@
+#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;
+}
diff --git a/servers/devman/devinfo.h b/servers/devman/devinfo.h
new file mode 100644 (file)
index 0000000..3ead931
--- /dev/null
@@ -0,0 +1,35 @@
+#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
diff --git a/servers/devman/devman.h b/servers/devman/devman.h
new file mode 100644 (file)
index 0000000..64d011a
--- /dev/null
@@ -0,0 +1,108 @@
+#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
diff --git a/servers/devman/main.c b/servers/devman/main.c
new file mode 100644 (file)
index 0000000..30a1852
--- /dev/null
@@ -0,0 +1,97 @@
+#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;
+}
+
diff --git a/servers/devman/proto.h b/servers/devman/proto.h
new file mode 100644 (file)
index 0000000..df54467
--- /dev/null
@@ -0,0 +1,23 @@
+#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 */
+