Added Device Manager (USB hotplug support)
This commit is contained in:
parent
bac0222eec
commit
00dcbb9dc6
12 changed files with 1170 additions and 2 deletions
|
@ -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 *
|
||||
*===========================================================================*/
|
||||
|
|
|
@ -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
|
||||
|
|
72
include/minix/devman.h
Normal file
72
include/minix/devman.h
Normal file
|
@ -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
|
|
@ -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
|
||||
|
||||
|
|
13
servers/devman/Makefile
Normal file
13
servers/devman/Makefile
Normal file
|
@ -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
|
105
servers/devman/bind.c
Normal file
105
servers/devman/bind.c
Normal file
|
@ -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;
|
||||
}
|
167
servers/devman/buf.c
Executable file
167
servers/devman/buf.c
Executable file
|
@ -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;
|
||||
}
|
521
servers/devman/device.c
Normal file
521
servers/devman/device.c
Normal file
|
@ -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;
|
||||
}
|
35
servers/devman/devinfo.h
Normal file
35
servers/devman/devinfo.h
Normal file
|
@ -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
|
108
servers/devman/devman.h
Normal file
108
servers/devman/devman.h
Normal file
|
@ -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
|
97
servers/devman/main.c
Normal file
97
servers/devman/main.c
Normal file
|
@ -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;
|
||||
}
|
||||
|
23
servers/devman/proto.h
Normal file
23
servers/devman/proto.h
Normal file
|
@ -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 */
|
||||
|
Loading…
Reference in a new issue