Skip to content

Commit

Permalink
dyn_dev: Make interface more generic
Browse files Browse the repository at this point in the history
  • Loading branch information
stek29 committed Aug 6, 2019
1 parent 995efea commit c7dc14a
Show file tree
Hide file tree
Showing 3 changed files with 68 additions and 36 deletions.
2 changes: 1 addition & 1 deletion fs/dev.c
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ struct dev_ops *char_devs[256] = {
[5] = &tty_dev,
[TTY_PSEUDO_MASTER_MAJOR] = &tty_dev,
[TTY_PSEUDO_SLAVE_MAJOR] = &tty_dev,
[DYN_DEV_MAJOR] = &dyn_dev,
[DYN_DEV_MAJOR] = &dyn_dev_char,
};

int dev_open(int major, int minor, int type, struct fd *fd) {
Expand Down
69 changes: 45 additions & 24 deletions fs/dyndev.c
Original file line number Diff line number Diff line change
Expand Up @@ -2,40 +2,57 @@
#include "fs/dev.h"
#include "fs/dyndev.h"


#define MAX_MINOR 255

// Handles DYNDEV_MAJOR device number
// XXX: unregister might be added later
// XXX(stek29): unregister might be added later
struct dyn_dev_info {
// devs & next_dev lock
lock_t devs_lock;
// table of dev_ops registered by minor number
struct dev_ops* devs[256];
// next free devs slot
// it's easier to detect overflow with u16 than with u8
uint16_t next_dev;
} dyn_info;

int dyn_dev_register(struct dev_ops *ops) {
lock(&dyn_info.devs_lock);

int minor = dyn_info.next_dev;
// Only up to 256 devices can be registered
if (minor == 256) {
unlock(&dyn_info.devs_lock);
return _ENOSPC;
struct dev_ops *devs[MAX_MINOR+1];
};

struct dyn_dev_info dyn_info_char = {
.devs_lock = LOCK_INITIALIZER,
.devs = {},
};

int dyn_dev_register(struct dev_ops *ops, int type, int major, int minor) {
// Validate arguments
if (minor < 0 || minor > MAX_MINOR) {
return _EINVAL;
}
if (major != DYN_DEV_MAJOR) {
return _EINVAL;
}
if (ops == NULL) {
return _EINVAL;
}
if (type != DYN_DEV_TYPE_CHAR) {
return _EINVAL;
}

++dyn_info.next_dev;
lock(&dyn_info_char.devs_lock);

assert(dyn_info.devs[minor] == NULL);
dyn_info.devs[minor] = ops;
// Make sure minor number isn't taken yet
if (dyn_info_char.devs[minor] != NULL) {
unlock(&dyn_info_char.devs_lock);
return _EEXIST;
}

unlock(&dyn_info.devs_lock);
return minor;
dyn_info_char.devs[minor] = ops;
unlock(&dyn_info_char.devs_lock);

return 0;
}

static int dyn_open(int major, int minor, struct fd *fd) {
static int dyn_open(int type, int major, int minor, struct fd *fd) {
assert(type == DYN_DEV_TYPE_CHAR);
assert(major == DYN_DEV_MAJOR);
// it's safe to access devs without locking (read-only)
struct dev_ops *ops = dyn_info.devs[minor];
struct dev_ops *ops = dyn_info_char.devs[minor];
if (ops == NULL) {
return _ENXIO;
}
Expand All @@ -46,6 +63,10 @@ static int dyn_open(int major, int minor, struct fd *fd) {
return 0;
return ops->open(major, minor, fd);
}
struct dev_ops dyn_dev = {
.open = dyn_open,

static int dyn_open_char(int major, int minor, struct fd *fd) {
return dyn_open(DYN_DEV_TYPE_CHAR, major, minor, fd);
}
struct dev_ops dyn_dev_char = {
.open = dyn_open_char,
};
33 changes: 22 additions & 11 deletions fs/dyndev.h
Original file line number Diff line number Diff line change
@@ -1,24 +1,35 @@
#ifndef FS_DYN_DEV_H
#define FS_DYN_DEV_H

// dyn_devs are dynamically added devices (character only for now)
// dyn_dev's are dynamically added devices (character only for now)
// with custom dev_ops assigned
// It's useful to add new device "drivers" in runtime (for example,
// devices only present on some platforms)
#define DYN_DEV_MAJOR 240

// (int)('D'+'Y') == 157
#define DYN_DEV_MAJOR 157
// dev_ops handing char device with DYN_DEV_MAJOR major number
extern struct dev_ops dyn_dev_char;

// dev_ops handing DYN_DEV_MAJOR major number
extern struct dev_ops dyn_dev;
#define DYN_DEV_TYPE_CHAR 0
#define DYN_DEV_TYPE_BLOCK 1

// Registeres new device with major number DYN_DEV_MAJOR and returned minor number
// handled by provided ops, which should be valid for "kernel" lifetime (should
// not be freed, might be static)
// Registeres new block/character device with provided major and
// minor numbers, handled by provided ops
//
// ops should be valid for "kernel" lifetime (should not be freed, but
// might be static), and should not be null
//
// type is DYN_DEV_TYPE_CHAR or DYN_DEV_TYPE_BLOCK
// (only char is supported for now)
//
// major should be DYN_DEV_MAJOR
//
// minor should be 0-255
//
// Return value:
// - newly registered device minor number (>= 0) on success
// - _ENOSPC if there are no free minor device numbers left
extern int dyn_dev_register(struct dev_ops *ops);
// - 0 on success
// - _EEXIST if provided minor number is alredy taken
// - _EINVAL if provided arguments are invalid
extern int dyn_dev_register(struct dev_ops *ops, int type, int major, int minor);

#endif

0 comments on commit c7dc14a

Please sign in to comment.