From 00dcbb9dc64384f6bd51ead98c5a1a7ffb69681c Mon Sep 17 00:00:00 2001 From: Dirk Vogt Date: Wed, 23 Feb 2011 13:48:03 +0000 Subject: [PATCH] Added Device Manager (USB hotplug support) --- common/include/minix/com.h | 27 ++ include/Makefile | 2 +- include/minix/devman.h | 72 +++++ servers/Makefile | 2 +- servers/devman/Makefile | 13 + servers/devman/bind.c | 105 ++++++++ servers/devman/buf.c | 167 ++++++++++++ servers/devman/device.c | 521 +++++++++++++++++++++++++++++++++++++ servers/devman/devinfo.h | 35 +++ servers/devman/devman.h | 108 ++++++++ servers/devman/main.c | 97 +++++++ servers/devman/proto.h | 23 ++ 12 files changed, 1170 insertions(+), 2 deletions(-) create mode 100644 include/minix/devman.h create mode 100644 servers/devman/Makefile create mode 100644 servers/devman/bind.c create mode 100755 servers/devman/buf.c create mode 100644 servers/devman/device.c create mode 100644 servers/devman/devinfo.h create mode 100644 servers/devman/devman.h create mode 100644 servers/devman/main.c create mode 100644 servers/devman/proto.h diff --git a/common/include/minix/com.h b/common/include/minix/com.h index 9094b3bbd..ffe9bdaeb 100644 --- a/common/include/minix/com.h +++ b/common/include/minix/com.h @@ -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. @@ -1203,6 +1204,32 @@ # 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 * *===========================================================================*/ diff --git a/include/Makefile b/include/Makefile index fdc087331..708955aa1 100644 --- a/include/Makefile +++ b/include/Makefile @@ -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 index 000000000..48ccb7f59 --- /dev/null +++ b/include/minix/devman.h @@ -0,0 +1,72 @@ +#ifndef MINIX_LIBDEVMAN_H +#define MINIX_LIBDEVMAN_H +#include +#include +#include + +/* 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 diff --git a/servers/Makefile b/servers/Makefile index 72fac3b8a..25aaf6493 100644 --- a/servers/Makefile +++ b/servers/Makefile @@ -4,7 +4,7 @@ .include 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 index 000000000..1a6840e66 --- /dev/null +++ b/servers/devman/Makefile @@ -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 +CFLAGS += -Wall diff --git a/servers/devman/bind.c b/servers/devman/bind.c new file mode 100644 index 000000000..c81baf828 --- /dev/null +++ b/servers/devman/bind.c @@ -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 index 000000000..c6f1f700f --- /dev/null +++ b/servers/devman/buf.c @@ -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 +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include + +#include + + +#include +#include +#include +#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 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 index 000000000..fa502c8e1 --- /dev/null +++ b/servers/devman/device.c @@ -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 index 000000000..3ead931d3 --- /dev/null +++ b/servers/devman/devinfo.h @@ -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 index 000000000..64d011a76 --- /dev/null +++ b/servers/devman/devman.h @@ -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 +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include + +#include +#include + +#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 index 000000000..30a18529a --- /dev/null +++ b/servers/devman/main.c @@ -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 +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + + +#include +#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 index 000000000..df5446727 --- /dev/null +++ b/servers/devman/proto.h @@ -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 */ +