libchardriver: full API rewrite

The new API now covers the entire character driver protocol, while
hiding all the message details. It should therefore be used by all
new character drivers. All existing drivers that already made use of
libchardriver have been changed to use the new API.

As one of the most important API changes, support for scatter and
gather transfers has been removed, as several key drivers already
did not support this, and it could be supported at the safecopy
level instead (for a future readv/writev).

Additional changes include:

- respond to block device open requests to avoid hanging VFS threads;
- add support for sef_cancel.

Change-Id: I1bab6c1cb66916c71b87aeb1db54a9bdf171fe6b
This commit is contained in:
David van Moolenbroek 2013-09-03 01:49:38 +02:00 committed by Lionel Sambuc
parent b0ea2920e6
commit 597151d963
14 changed files with 958 additions and 1198 deletions

View file

@ -151,11 +151,9 @@ static int read_cal_coef(void);
static int measure(int32_t * temperature, int32_t * pressure);
/* libchardriver callbacks */
static struct device *bmp085_prepare(dev_t UNUSED(dev));
static int bmp085_transfer(endpoint_t endpt, int opcode, u64_t position,
iovec_t * iov, unsigned nr_req, endpoint_t UNUSED(user_endpt),
unsigned int UNUSED(flags));
static int bmp085_other(message * m);
static ssize_t bmp085_read(devminor_t minor, u64_t position, endpoint_t endpt,
cp_grant_id_t grant, size_t size, int flags, cdev_id_t id);
static void bmp085_other(message * m, int ipc_status);
/* SEF Function */
static int sef_cb_lu_state_save(int);
@ -165,21 +163,8 @@ static void sef_local_startup(void);
/* Entry points to this driver from libchardriver. */
static struct chardriver bmp085_tab = {
.cdr_open = do_nop,
.cdr_close = do_nop,
.cdr_ioctl = nop_ioctl,
.cdr_prepare = bmp085_prepare,
.cdr_transfer = bmp085_transfer,
.cdr_cleanup = nop_cleanup,
.cdr_alarm = nop_alarm,
.cdr_cancel = nop_cancel,
.cdr_select = nop_select,
.cdr_other = bmp085_other
};
static struct device bmp085_device = {
.dv_base = 0,
.dv_size = 0
.cdr_read = bmp085_read,
.cdr_other = bmp085_other
};
/*
@ -438,18 +423,12 @@ measure(int32_t * temperature, int32_t * pressure)
return OK;
}
static struct device *
bmp085_prepare(dev_t UNUSED(dev))
static ssize_t
bmp085_read(devminor_t UNUSED(minor), u64_t position, endpoint_t endpt,
cp_grant_id_t grant, size_t size, int UNUSED(flags), cdev_id_t UNUSED(id))
{
return &bmp085_device;
}
static int
bmp085_transfer(endpoint_t endpt, int opcode, u64_t position,
iovec_t * iov, unsigned nr_req, endpoint_t UNUSED(user_endpt),
unsigned int UNUSED(flags))
{
int bytes, r;
u64_t dev_size;
int r;
uint32_t temperature, pressure;
r = measure(&temperature, &pressure);
@ -464,48 +443,33 @@ bmp085_transfer(endpoint_t endpt, int opcode, u64_t position,
log_trace(&log, "%s", buffer);
bytes = strlen(buffer) - position < iov->iov_size ?
strlen(buffer) - position : iov->iov_size;
dev_size = (u64_t)strlen(buffer);
if (position >= dev_size) return 0;
if (position + size > dev_size)
size = (size_t)(dev_size - position);
if (bytes <= 0) {
return OK;
}
r = sys_safecopyto(endpt, grant, 0,
(vir_bytes)(buffer + (size_t)position), size);
switch (opcode) {
case DEV_GATHER_S:
r = sys_safecopyto(endpt, (cp_grant_id_t) iov->iov_addr, 0,
(vir_bytes) (buffer + position), bytes);
iov->iov_size -= bytes;
break;
default:
return EINVAL;
}
return r;
return (r != OK) ? r : size;
}
static int
bmp085_other(message * m)
static void
bmp085_other(message * m, int ipc_status)
{
int r;
switch (m->m_type) {
case NOTIFY_MESSAGE:
if (is_ipc_notify(ipc_status)) {
if (m->m_source == DS_PROC_NR) {
log_debug(&log,
"bus driver changed state, update endpoint\n");
i2cdriver_handle_bus_update(&bus_endpoint, bus,
address);
}
r = OK;
break;
default:
log_warn(&log, "Invalid message type (0x%x)\n", m->m_type);
r = EINVAL;
break;
return;
}
return r;
log_warn(&log, "Invalid message type (0x%x)\n", m->m_type);
}
static int

View file

@ -312,7 +312,7 @@ arch_pan_display(int minor, struct fb_var_screeninfo *fbvsp)
}
int
arch_fb_init(int minor, struct device *dev, struct edid_info *info)
arch_fb_init(int minor, struct edid_info *info)
{
int r;
u32_t rdispc;
@ -320,13 +320,9 @@ arch_fb_init(int minor, struct device *dev, struct edid_info *info)
const struct panel_config *panel_cfg = &omap_cfg[minor];
assert(dev != NULL);
if (minor != 0) return ENXIO; /* We support only one minor */
if (initialized) {
dev->dv_base = fb_vir;
dev->dv_size = fb_size;
return OK;
} else if (info != NULL) {
log_debug(&log, "Configuring Settings based on EDID...\n");
@ -403,8 +399,6 @@ arch_fb_init(int minor, struct device *dev, struct edid_info *info)
if (fb_vir == (vir_bytes) MAP_FAILED) {
panic("Unable to allocate contiguous memory\n");
}
dev->dv_base = fb_vir;
dev->dv_size = fb_size;
/* Configure buffer settings and turn on LCD/Digital */
arch_configure_display(minor);

View file

@ -23,17 +23,14 @@
/*
* Function prototypes for the fb driver.
*/
static int fb_open(message *m);
static int fb_close(message *m);
static struct device * fb_prepare(dev_t device);
static int fb_transfer(endpoint_t endpt, int opcode, u64_t position,
iovec_t *iov, unsigned int nr_req, endpoint_t user_endpt, unsigned int
flags);
static int fb_do_read(endpoint_t ep, iovec_t *iov, int minor, u64_t pos,
size_t *io_bytes);
static int fb_do_write(endpoint_t ep, iovec_t *iov, int minor, u64_t pos,
size_t *io_bytes);
static int fb_ioctl(message *m);
static int fb_open(devminor_t minor, int access, endpoint_t user_endpt);
static int fb_close(devminor_t minor);
static ssize_t fb_read(devminor_t minor, u64_t pos, endpoint_t ep,
cp_grant_id_t gid, size_t size, int flags, cdev_id_t id);
static ssize_t fb_write(devminor_t minor, u64_t pos, endpoint_t ep,
cp_grant_id_t gid, size_t size, int flags, cdev_id_t id);
static int fb_ioctl(devminor_t minor, unsigned long request, endpoint_t ep,
cp_grant_id_t gid, int flags, endpoint_t user_ep, cdev_id_t id);
static void paint_bootlogo(int minor);
static void paint_restartlogo(int minor);
static void paint_centered(int minor, char *data, int width, int height);
@ -52,143 +49,90 @@ static int lu_state_restore(void);
/* Entry points to the fb driver. */
static struct chardriver fb_tab =
{
fb_open,
fb_close,
fb_ioctl,
fb_prepare,
fb_transfer,
nop_cleanup,
nop_alarm,
nop_cancel,
nop_select,
NULL
.cdr_open = fb_open,
.cdr_close = fb_close,
.cdr_read = fb_read,
.cdr_write = fb_write,
.cdr_ioctl = fb_ioctl
};
/** Represents the /dev/fb device. */
static struct device fb_device[FB_DEV_NR];
static int fb_minor, has_restarted = 0;
static int has_restarted = 0;
static u64_t has_restarted_t1, has_restarted_t2;
static int open_counter[FB_DEV_NR]; /* Open count */
static int
fb_open(message *m)
fb_open(devminor_t minor, int UNUSED(access), endpoint_t UNUSED(user_endpt))
{
int r;
static int initialized = 0;
static struct edid_info info;
static struct edid_info *infop = NULL;
if (m->DEVICE < 0 || m->DEVICE >= FB_DEV_NR) return ENXIO;
if (minor < 0 || minor >= FB_DEV_NR) return ENXIO;
if (!initialized) {
r = fb_edid_read(m->DEVICE, &info);
r = fb_edid_read(minor, &info);
infop = (r == 0) ? &info : NULL;
}
if (arch_fb_init(m->DEVICE, &fb_device[m->DEVICE], infop) == OK) {
open_counter[m->DEVICE]++;
if (arch_fb_init(minor, infop) == OK) {
open_counter[minor]++;
if (!initialized) {
if (has_restarted) {
read_frclock_64(&has_restarted_t1);
paint_restartlogo(m->DEVICE);
paint_restartlogo(minor);
} else {
paint_bootlogo(m->DEVICE);
paint_bootlogo(minor);
}
initialized = 1;
}
return OK;
}
return ENXIO ;
return ENXIO;
}
static int
fb_close(message *m)
fb_close(devminor_t minor)
{
if (m->DEVICE < 0 || m->DEVICE >= FB_DEV_NR) return ENXIO;
assert(open_counter[m->DEVICE] > 0);
open_counter[m->DEVICE]--;
if (minor < 0 || minor >= FB_DEV_NR) return ENXIO;
assert(open_counter[minor] > 0);
open_counter[minor]--;
return OK;
}
static struct device *
fb_prepare(dev_t dev)
{
if (dev < 0 || dev >= FB_DEV_NR) return NULL;
assert(open_counter[dev] > 0);
fb_minor = dev;
return &fb_device[dev];
}
static int
fb_transfer(endpoint_t endpt, int opcode, u64_t position,
iovec_t *iov, unsigned nr_req, endpoint_t UNUSED(user_endpt),
unsigned int UNUSED(flags))
{
size_t io_bytes = 0, ret;
if (nr_req != 1) {
/* This should never trigger for char drivers at the moment. */
printf("fb: vectored transfer, using first element only\n");
}
switch (opcode) {
case DEV_GATHER_S:
/* Userland read operation */
ret = fb_do_read(endpt, iov, fb_minor, position, &io_bytes);
iov->iov_size -= io_bytes;
break;
case DEV_SCATTER_S:
/* Userland write operation */
ret = fb_do_write(endpt, iov, fb_minor, position, &io_bytes);
iov->iov_size -= io_bytes;
break;
default:
return EINVAL;
}
return ret;
}
static int
fb_do_read(endpoint_t ep, iovec_t *iov, int minor, u64_t pos, size_t *io_bytes)
static ssize_t
fb_read(devminor_t minor, u64_t pos, endpoint_t ep, cp_grant_id_t gid,
size_t size, int UNUSED(flags), cdev_id_t UNUSED(id))
{
struct device dev;
int r;
if (minor < 0 || minor >= FB_DEV_NR) return ENXIO;
assert(open_counter[minor] > 0);
arch_get_device(minor, &dev);
if (pos >= dev.dv_size) return EINVAL;
if (size == 0 || pos >= dev.dv_size) return 0;
if (pos + size > dev.dv_size)
size = (size_t)(dev.dv_size - pos);
if (dev.dv_size - pos < iov->iov_size) {
*io_bytes = dev.dv_size - pos;
} else {
*io_bytes = iov->iov_size;
}
r = sys_safecopyto(ep, gid, 0, (vir_bytes)(dev.dv_base + (size_t)pos),
size);
if (*io_bytes <= 0) {
return OK;
}
return sys_safecopyto(ep, (cp_grant_id_t) iov->iov_addr, 0,
(vir_bytes) (dev.dv_base + ex64lo(pos)),
*io_bytes);
return (r != OK) ? r : size;
}
static int
fb_ioctl(message *m)
fb_ioctl(devminor_t minor, unsigned long request, endpoint_t ep,
cp_grant_id_t gid, int UNUSED(flags), endpoint_t UNUSED(user_ep),
cdev_id_t UNUSED(id))
{
/* Process I/O control requests */
endpoint_t ep;
cp_grant_id_t gid;
int minor;
unsigned int request;
int r;
minor = m->DEVICE;
request = m->COUNT;
ep = (endpoint_t) m->USER_ENDPT;
gid = (cp_grant_id_t) m->IO_GRANT;
if (minor != 0) return EINVAL;
if (minor < 0 || minor >= FB_DEV_NR) return ENXIO;
switch(request) {
case FBIOGET_VSCREENINFO:
@ -205,7 +149,7 @@ fb_ioctl(message *m)
return r;
}
return EINVAL;
return ENOTTY;
}
static int
@ -270,34 +214,29 @@ do_get_fixscreeninfo(int minor, endpoint_t ep, cp_grant_id_t gid)
return r;
}
static int
fb_do_write(endpoint_t ep, iovec_t *iov, int minor, u64_t pos, size_t *io_bytes)
static ssize_t
fb_write(devminor_t minor, u64_t pos, endpoint_t ep, cp_grant_id_t gid,
size_t size, int UNUSED(flags), cdev_id_t UNUSED(id))
{
struct device dev;
int r;
if (minor < 0 || minor >= FB_DEV_NR) return ENXIO;
assert(open_counter[minor] > 0);
if (has_restarted && keep_displaying_restarted())
return EAGAIN;
arch_get_device(minor, &dev);
if (pos >= dev.dv_size) {
return EINVAL;
}
if (size == 0 || pos >= dev.dv_size) return 0;
if (pos + size > dev.dv_size)
size = (size_t)(dev.dv_size - pos);
if (dev.dv_size - pos < iov->iov_size) {
*io_bytes = dev.dv_size - pos;
} else {
*io_bytes = iov->iov_size;
}
r = sys_safecopyfrom(ep, gid, 0,
(vir_bytes)(dev.dv_base + (size_t)pos), size);
if (*io_bytes <= 0) {
return OK;
}
if (has_restarted && keep_displaying_restarted()) {
return EAGAIN;
}
return sys_safecopyfrom(ep, (cp_grant_id_t) iov->iov_addr, 0,
(vir_bytes) (dev.dv_base + ex64lo(pos)),
*io_bytes);
return (r != OK) ? r : size;
}
static int

View file

@ -3,7 +3,7 @@
#include <minix/fb.h>
int arch_fb_init(int minor, struct device *dev, struct edid_info *info);
int arch_fb_init(int minor, struct edid_info *info);
int arch_get_device(int minor, struct device *dev);
int arch_get_varscreeninfo(int minor, struct fb_var_screeninfo *fbvsp);
int arch_put_varscreeninfo(int minor, struct fb_var_screeninfo *fbvs_copy);

View file

@ -8,12 +8,10 @@
/*
* Function prototypes for the hello driver.
*/
static int hello_open(message *m);
static int hello_close(message *m);
static struct device * hello_prepare(dev_t device);
static int hello_transfer(endpoint_t endpt, int opcode, u64_t position,
iovec_t *iov, unsigned int nr_req, endpoint_t user_endpt, unsigned int
flags);
static int hello_open(devminor_t minor, int access, endpoint_t user_endpt);
static int hello_close(devminor_t minor);
static ssize_t hello_read(devminor_t minor, u64_t position, endpoint_t endpt,
cp_grant_id_t grant, size_t size, int flags, cdev_id_t id);
/* SEF functions and variables. */
static void sef_local_startup(void);
@ -24,77 +22,54 @@ static int lu_state_restore(void);
/* Entry points to the hello driver. */
static struct chardriver hello_tab =
{
hello_open,
hello_close,
nop_ioctl,
hello_prepare,
hello_transfer,
nop_cleanup,
nop_alarm,
nop_cancel,
nop_select,
NULL
.cdr_open = hello_open,
.cdr_close = hello_close,
.cdr_read = hello_read,
};
/** Represents the /dev/hello device. */
static struct device hello_device;
/** State variable to count the number of times the device has been opened. */
/** State variable to count the number of times the device has been opened.
* Note that this is not the regular type of open counter: it never decreases.
*/
static int open_counter;
static int hello_open(message *UNUSED(m))
static int hello_open(devminor_t UNUSED(minor), int UNUSED(access),
endpoint_t UNUSED(user_endpt))
{
printf("hello_open(). Called %d time(s).\n", ++open_counter);
return OK;
}
static int hello_close(message *UNUSED(m))
static int hello_close(devminor_t UNUSED(minor))
{
printf("hello_close()\n");
return OK;
}
static struct device * hello_prepare(dev_t UNUSED(dev))
static ssize_t hello_read(devminor_t UNUSED(minor), u64_t position,
endpoint_t endpt, cp_grant_id_t grant, size_t size, int UNUSED(flags),
cdev_id_t UNUSED(id))
{
hello_device.dv_base = make64(0, 0);
hello_device.dv_size = make64(strlen(HELLO_MESSAGE), 0);
return &hello_device;
}
u64_t dev_size;
char *ptr;
int ret;
static int hello_transfer(endpoint_t endpt, int opcode, u64_t position,
iovec_t *iov, unsigned nr_req, endpoint_t UNUSED(user_endpt),
unsigned int UNUSED(flags))
{
int bytes, ret;
printf("hello_read()\n");
printf("hello_transfer()\n");
/* This is the total size of our device. */
dev_size = (u64_t) strlen(HELLO_MESSAGE);
if (nr_req != 1)
{
/* This should never trigger for character drivers at the moment. */
printf("HELLO: vectored transfer request, using first element only\n");
}
/* Check for EOF, and possibly limit the read size. */
if (position >= dev_size) return 0; /* EOF */
if (position + size > dev_size)
size = (size_t)(dev_size - position); /* limit size */
bytes = strlen(HELLO_MESSAGE) - ex64lo(position) < iov->iov_size ?
strlen(HELLO_MESSAGE) - ex64lo(position) : iov->iov_size;
/* Copy the requested part to the caller. */
ptr = HELLO_MESSAGE + (size_t)position;
if ((ret = sys_safecopyto(endpt, grant, 0, (vir_bytes) ptr, size)) != OK)
return ret;
if (bytes <= 0)
{
return OK;
}
switch (opcode)
{
case DEV_GATHER_S:
ret = sys_safecopyto(endpt, (cp_grant_id_t) iov->iov_addr, 0,
(vir_bytes) (HELLO_MESSAGE + ex64lo(position)),
bytes);
iov->iov_size -= bytes;
break;
default:
return EINVAL;
}
return ret;
/* Return the number of bytes read. */
return size;
}
static int sef_cb_lu_state_save(int UNUSED(state)) {

View file

@ -39,11 +39,9 @@ static int do_i2c_ioctl_exec(endpoint_t caller, cp_grant_id_t grant_nr);
static int env_parse_instance(void);
/* libchardriver callbacks */
int i2c_ioctl(message * m);
struct device *i2c_prepare(dev_t dev);
int i2c_transfer(endpoint_t endpt, int opcode, u64_t position,
iovec_t * iov, unsigned nr_req, endpoint_t user_endpt, unsigned int flags);
int i2c_other(message * m);
static int i2c_ioctl(devminor_t minor, unsigned long request, endpoint_t endpt,
cp_grant_id_t grant, int flags, endpoint_t user_endpt, cdev_id_t id);
static void i2c_other(message * m, int ipc_status);
/* SEF callbacks and driver state management */
static int sef_cb_lu_state_save(int);
@ -69,8 +67,6 @@ static struct i2cdev
*/
int (*process) (minix_i2c_ioctl_exec_t * ioctl_exec);
struct device i2c_device;
/* logging - use with log_warn(), log_info(), log_debug(), log_trace() */
static struct log log = {
.name = "i2c",
@ -82,16 +78,8 @@ static struct log log = {
* Only i2c_ioctl() and i2c_other() are implemented. The rest are no-op.
*/
static struct chardriver i2c_tab = {
.cdr_open = do_nop,
.cdr_close = do_nop,
.cdr_ioctl = i2c_ioctl,
.cdr_prepare = i2c_prepare,
.cdr_transfer = i2c_transfer,
.cdr_cleanup = nop_cleanup,
.cdr_alarm = nop_alarm,
.cdr_cancel = nop_cancel,
.cdr_select = nop_select,
.cdr_other = i2c_other
.cdr_ioctl = i2c_ioctl,
.cdr_other = i2c_other
};
/*
@ -233,14 +221,14 @@ validate_ioctl_exec(minix_i2c_ioctl_exec_t * ioctl_exec)
}
len = ioctl_exec->iie_cmdlen;
if (len < 0 || len > I2C_EXEC_MAX_CMDLEN) {
if (len > I2C_EXEC_MAX_CMDLEN) {
log_warn(&log,
"iie_cmdlen out of range 0-I2C_EXEC_MAX_CMDLEN\n");
return EINVAL;
}
len = ioctl_exec->iie_buflen;
if (len < 0 || len > I2C_EXEC_MAX_BUFLEN) {
if (len > I2C_EXEC_MAX_BUFLEN) {
log_warn(&log,
"iie_buflen out of range 0-I2C_EXEC_MAX_BUFLEN\n");
return EINVAL;
@ -298,30 +286,40 @@ do_i2c_ioctl_exec(endpoint_t caller, cp_grant_id_t grant_nr)
return OK;
}
int
i2c_ioctl(message * m)
static int
i2c_ioctl(devminor_t UNUSED(minor), unsigned long request, endpoint_t endpt,
cp_grant_id_t grant, int UNUSED(flags), endpoint_t UNUSED(user_endpt),
cdev_id_t UNUSED(id))
{
int r;
switch (m->COUNT) {
switch (request) {
case MINIX_I2C_IOCTL_EXEC:
r = do_i2c_ioctl_exec(m->m_source, (cp_grant_id_t)m->IO_GRANT);
r = do_i2c_ioctl_exec(endpt, grant);
break;
default:
log_warn(&log, "Invalid ioctl() 0x%x\n", m->COUNT);
r = EINVAL;
log_warn(&log, "Invalid ioctl() 0x%x\n", request);
r = ENOTTY;
break;
}
return r;
}
int
i2c_other(message * m)
static void
i2c_other(message * m, int ipc_status)
{
message m_reply;
int r;
if (is_ipc_notify(ipc_status)) {
/* handle notifications about drivers changing state */
if (m->m_source == DS_PROC_NR) {
ds_event();
}
return;
}
switch (m->m_type) {
case BUSC_I2C_RESERVE:
/* reserve a device on the bus for exclusive access */
@ -331,12 +329,6 @@ i2c_other(message * m)
/* handle request from another driver */
r = do_i2c_ioctl_exec(m->m_source, m->BUSC_I2C_GRANT);
break;
case NOTIFY_MESSAGE:
/* handle notifications about drivers changing state */
if (m->m_source == DS_PROC_NR) {
ds_event();
}
return EDONTREPLY;
default:
log_warn(&log, "Invalid message type (0x%x)\n", m->m_type);
r = EINVAL;
@ -345,32 +337,12 @@ i2c_other(message * m)
log_trace(&log, "i2c_other() returning r=%d\n", r);
/* We cannot use libchardriver to reply, as it will send DEV_REVIVE. */
/* Send a reply. */
memset(&m_reply, 0, sizeof(m_reply));
m_reply.m_type = r;
if ((r = send(m->m_source, &m_reply)) != OK)
log_warn(&log, "send() to %d failed: %d\n", m->m_source, r);
return EDONTREPLY;
}
struct device *
i2c_prepare(dev_t dev)
{
/* NOP */
i2c_device.dv_base = make64(0, 0);
i2c_device.dv_size = make64(0, 0);
return &i2c_device;
}
int
i2c_transfer(endpoint_t endpt, int opcode, u64_t position,
iovec_t * iov, unsigned nr_req, endpoint_t user_endpt, unsigned int flags)
{
/* NOP */
return OK;
}
/*

View file

@ -19,31 +19,24 @@
#define LOGINC(n, i) do { (n) = (((n) + (i)) % LOG_SIZE); } while(0)
struct logdevice logdevices[NR_DEVS];
static struct device log_geom[NR_DEVS]; /* base and size of devices */
static int log_device = -1; /* current device */
static struct device *log_prepare(dev_t device);
static int log_transfer(endpoint_t endpt, int opcode, u64_t position,
iovec_t *iov, unsigned int nr_req, endpoint_t user_endpt, unsigned int
flags);
static int log_do_open(message *m_ptr);
static int log_cancel(message *m_ptr);
static int log_select(message *m_ptr);
static int subread(struct logdevice *log, int count, endpoint_t endpt,
cp_grant_id_t grant, size_t);
static ssize_t log_read(devminor_t minor, u64_t position, endpoint_t endpt,
cp_grant_id_t grant, size_t size, int flags, cdev_id_t id);
static ssize_t log_write(devminor_t minor, u64_t position, endpoint_t endpt,
cp_grant_id_t grant, size_t size, int flags, cdev_id_t id);
static int log_open(devminor_t minor, int access, endpoint_t user_endpt);
static int log_cancel(devminor_t minor, endpoint_t endpt, cdev_id_t id);
static int log_select(devminor_t minor, unsigned int ops, endpoint_t endpt);
static int subread(struct logdevice *log, size_t size, endpoint_t endpt,
cp_grant_id_t grant);
/* Entry points to this driver. */
static struct chardriver log_dtab = {
log_do_open, /* open or mount */
do_nop, /* nothing on a close */
nop_ioctl, /* ioctl nop */
log_prepare, /* prepare for I/O on a given minor device */
log_transfer, /* do the I/O */
nop_cleanup, /* no need to clean up */
nop_alarm, /* no alarm */
log_cancel, /* CANCEL request */
log_select, /* DEV_SELECT request */
NULL /* Unrecognized messages */
.cdr_open = log_open,
.cdr_read = log_read,
.cdr_write = log_write,
.cdr_cancel = log_cancel,
.cdr_select = log_select
};
/* SEF functions and variables. */
@ -100,15 +93,10 @@ static int sef_cb_init_fresh(int UNUSED(type), sef_init_info_t *UNUSED(info))
/* Initialize log devices. */
for(i = 0; i < NR_DEVS; i++) {
log_geom[i].dv_size = ((u64_t)(LOG_SIZE));
log_geom[i].dv_base = ((u64_t)((long)logdevices[i].log_buffer));
logdevices[i].log_size = logdevices[i].log_read =
logdevices[i].log_write =
logdevices[i].log_select_alerted =
logdevices[i].log_selected =
logdevices[i].log_select_ready_ops = 0;
logdevices[i].log_selected = 0;
logdevices[i].log_source = NONE;
logdevices[i].log_revive_alerted = 0;
}
return(OK);
@ -125,29 +113,24 @@ static void sef_cb_signal_handler(int signo)
do_new_kmess();
}
/*===========================================================================*
* log_prepare *
*===========================================================================*/
static struct device *log_prepare(dev_t device)
{
/* Prepare for I/O on a device: check if the minor device number is ok. */
if (device >= NR_DEVS) return(NULL);
log_device = (int) device;
return(&log_geom[device]);
}
/*===========================================================================*
* subwrite *
*===========================================================================*/
static int
subwrite(struct logdevice *log, int count, endpoint_t endpt,
cp_grant_id_t grant, size_t offset, char *localbuf)
subwrite(struct logdevice *log, size_t size, endpoint_t endpt,
cp_grant_id_t grant, char *localbuf)
{
int d, r;
char *buf;
message m;
size_t count, offset;
int overflow, r;
devminor_t minor;
char *buf;
message m;
/* With a sufficiently large input size, we might wrap around the ring buffer
* multiple times.
*/
for (offset = 0; offset < size; offset += count) {
count = size - offset;
if (log->log_write + count > LOG_SIZE)
count = LOG_SIZE - log->log_write;
@ -155,75 +138,49 @@ subwrite(struct logdevice *log, int count, endpoint_t endpt,
if(localbuf != NULL) {
memcpy(buf, localbuf, count);
localbuf += count;
}
else {
if((r=sys_safecopyfrom(endpt, grant, offset,
(vir_bytes)buf, count)) != OK)
return r;
break; /* do process partial write upon error */
}
LOGINC(log->log_write, count);
log->log_size += count;
if(log->log_size > LOG_SIZE) {
int overflow;
overflow = log->log_size - LOG_SIZE;
log->log_size -= overflow;
LOGINC(log->log_read, overflow);
}
if(log->log_size > 0 && log->log_source != NONE &&
!log->log_revive_alerted) {
/* Someone who was suspended on read can now
* be revived.
*/
log->log_status = subread(log, log->log_iosize,
log->log_source, log->log_user_grant,
log->log_user_offset);
r = offset; /* this will be the return value upon success */
}
m.m_type = DEV_REVIVE;
m.REP_ENDPT = log->log_proc_nr;
m.REP_STATUS = log->log_status;
m.REP_IO_GRANT = log->log_user_grant;
r= send(log->log_source, &m);
if (r != OK)
{
printf("log`subwrite: send to %d failed: %d\n",
log->log_source, r);
}
log->log_source = NONE;
}
if (log->log_size > 0 && log->log_source != NONE) {
/* Someone who was suspended on read can now be revived. */
r = subread(log, log->log_iosize, log->log_source, log->log_grant);
if(log->log_size > 0)
log->log_select_ready_ops |= SEL_RD;
chardriver_reply_task(log->log_source, log->log_id, r);
if(log->log_size > 0 && log->log_selected &&
!(log->log_select_alerted)) {
/* Someone(s) who was/were select()ing can now
* be awoken. If there was a blocking read (above),
* this can only happen if the blocking read didn't
* swallow all the data (log_size > 0).
*/
if(log->log_selected & SEL_RD) {
d= log-logdevices;
m.m_type = DEV_SEL_REPL2;
m.DEV_SEL_OPS = log->log_select_ready_ops;
m.DEV_MINOR = d;
log->log_source = NONE;
}
if (log->log_size > 0 && (log->log_selected & SEL_RD)) {
/* Someone(s) who was/were select()ing can now be awoken. If there was
* a blocking read (above), this can only happen if the blocking read
* didn't swallow all the data (log_size > 0).
*/
minor = log-logdevices;
#if LOG_DEBUG
printf("select sending DEV_SEL_REPL2\n");
printf("select sending DEV_SEL_REPL2\n");
#endif
r= send(log->log_select_proc, &m);
if (r != OK)
{
printf(
"log`subwrite: send to %d failed: %d\n",
log->log_select_proc, r);
}
log->log_selected &= ~log->log_select_ready_ops;
}
}
chardriver_reply_select(log->log_select_proc, minor, SEL_RD);
log->log_selected &= ~SEL_RD;
}
return count;
return r;
}
/*===========================================================================*
@ -232,189 +189,156 @@ subwrite(struct logdevice *log, int count, endpoint_t endpt,
void
log_append(char *buf, int count)
{
int w = 0, skip = 0;
int skip = 0;
if(count < 1) return;
if(count > LOG_SIZE) skip = count - LOG_SIZE;
count -= skip;
buf += skip;
w = subwrite(&logdevices[0], count, SELF, GRANT_INVALID, 0, buf);
if(count < 1) return;
if(count > LOG_SIZE) skip = count - LOG_SIZE;
count -= skip;
buf += skip;
if(w > 0 && w < count)
subwrite(&logdevices[0], count-w, SELF, GRANT_INVALID, 0,
buf + w);
return;
subwrite(&logdevices[0], count, SELF, GRANT_INVALID, buf);
}
/*===========================================================================*
* subread *
*===========================================================================*/
static int
subread(struct logdevice *log, int count, endpoint_t endpt,
cp_grant_id_t grant, size_t offset)
subread(struct logdevice *log, size_t size, endpoint_t endpt,
cp_grant_id_t grant)
{
char *buf;
int r;
size_t offset, count;
char *buf;
int r;
for (offset = 0; log->log_size > 0 && offset < size; offset += count) {
count = size - offset;
if (count > log->log_size)
count = log->log_size;
if (log->log_read + count > LOG_SIZE)
count = LOG_SIZE - log->log_read;
buf = log->log_buffer + log->log_read;
if((r=sys_safecopyto(endpt, grant, offset,
(vir_bytes)buf, count)) != OK)
if((r=sys_safecopyto(endpt, grant, offset, (vir_bytes)buf,
count)) != OK)
return r;
LOGINC(log->log_read, count);
log->log_size -= count;
}
return count;
return offset;
}
/*===========================================================================*
* log_transfer *
* log_read *
*===========================================================================*/
static int log_transfer(
endpoint_t endpt, /* endpoint of grant owner */
int opcode, /* DEV_GATHER_S or DEV_SCATTER_S */
u64_t UNUSED(position), /* offset on device to read or write */
iovec_t *iov, /* pointer to read or write request vector */
unsigned int nr_req, /* length of request vector */
endpoint_t user_endpt, /* endpoint of user process */
unsigned int flags
)
static ssize_t log_read(devminor_t minor, u64_t UNUSED(position),
endpoint_t endpt, cp_grant_id_t grant, size_t size, int flags,
cdev_id_t id)
{
/* Read or write one the driver's minor devices. */
int count;
cp_grant_id_t grant;
int accumulated_read = 0;
/* Read from one of the driver's minor devices. */
struct logdevice *log;
size_t vir_offset = 0;
int r;
if(log_device < 0 || log_device >= NR_DEVS)
return EIO;
if (minor < 0 || minor >= NR_DEVS) return EIO;
log = &logdevices[minor];
/* Get minor device number and check for /dev/null. */
log = &logdevices[log_device];
/* If there's already someone hanging to read, don't accept new work. */
if (log->log_source != NONE) return OK;
while (nr_req > 0) {
/* How much to transfer and where to / from. */
count = iov->iov_size;
grant = iov->iov_addr;
if (!log->log_size && size > 0) {
if (flags & FLG_OP_NONBLOCK) return EAGAIN;
switch (log_device) {
case MINOR_KLOG:
if (opcode == DEV_GATHER_S) {
if (log->log_source != NONE || count < 1) {
/* There's already someone hanging to read, or
* no real I/O requested.
*/
return(OK);
}
if (!log->log_size) {
if(accumulated_read)
return OK;
if (flags & FLG_OP_NONBLOCK)
return EAGAIN;
/* No data available; let caller block. */
log->log_source = endpt;
log->log_iosize = count;
log->log_user_grant = grant;
log->log_user_offset = 0;
log->log_revive_alerted = 0;
log->log_proc_nr = user_endpt;
/* No data available; let caller block. */
log->log_source = endpt;
log->log_iosize = size;
log->log_grant = grant;
log->log_id = id;
#if LOG_DEBUG
printf("blocked %d (%d)\n",
log->log_source, log->log_proc_nr);
printf("blocked %d (%d)\n", log->log_source, id);
#endif
return(EDONTREPLY);
}
count = subread(log, count, endpt, grant, vir_offset);
if(count < 0) {
return count;
}
accumulated_read += count;
} else {
count = subwrite(log, count, endpt, grant, vir_offset, NULL);
if(count < 0)
return count;
}
break;
/* Unknown (illegal) minor device. */
default:
return(EINVAL);
}
/* Book the number of bytes transferred. */
vir_offset += count;
if ((iov->iov_size -= count) == 0) { iov++; nr_req--; vir_offset = 0; }
return EDONTREPLY;
}
return(OK);
return subread(log, size, endpt, grant);
}
/*===========================================================================*
* log_write *
*===========================================================================*/
static ssize_t log_write(devminor_t minor, u64_t UNUSED(position),
endpoint_t endpt, cp_grant_id_t grant, size_t size, int UNUSED(flags),
cdev_id_t UNUSED(id))
{
/* Write to one of the driver's minor devices. */
struct logdevice *log;
int r;
if (minor < 0 || minor >= NR_DEVS) return EIO;
log = &logdevices[minor];
return subwrite(log, size, endpt, grant, NULL);
}
/*============================================================================*
* log_do_open *
* log_open *
*============================================================================*/
static int log_do_open(message *m_ptr)
static int log_open(devminor_t minor, int UNUSED(access),
endpoint_t UNUSED(user_endpt))
{
if (log_prepare(m_ptr->DEVICE) == NULL) return(ENXIO);
if (minor < 0 || minor >= NR_DEVS) return(ENXIO);
return(OK);
}
/*============================================================================*
* log_cancel *
*============================================================================*/
static int log_cancel(message *m_ptr)
static int log_cancel(devminor_t minor, endpoint_t endpt, cdev_id_t id)
{
int d;
d = m_ptr->DEVICE;
if(d < 0 || d >= NR_DEVS)
if (minor < 0 || minor >= NR_DEVS)
return EINVAL;
if (m_ptr->USER_ENDPT != logdevices[d].log_proc_nr)
/* Not for the suspended request? Must be a stale cancel request. Ignore. */
if (logdevices[minor].log_source != endpt || logdevices[minor].log_id != id)
return EDONTREPLY;
if ((cp_grant_id_t) m_ptr->IO_GRANT != logdevices[d].log_user_grant)
return EDONTREPLY;
logdevices[d].log_proc_nr = NONE;
logdevices[d].log_revive_alerted = 0;
return(OK);
logdevices[minor].log_source = NONE;
return EINTR; /* this is the reply to the original, interrupted request */
}
/*============================================================================*
* log_select *
*============================================================================*/
static int log_select(message *m_ptr)
static int log_select(devminor_t minor, unsigned int ops, endpoint_t endpt)
{
int d, ready_ops = 0, ops = 0;
d = m_ptr->DEV_MINOR;
if(d < 0 || d >= NR_DEVS) {
#if LOG_DEBUG
printf("line %d? EINVAL\n", d);
#endif
return EINVAL;
}
int want_ops, ready_ops = 0;
ops = m_ptr->DEV_SEL_OPS & (SEL_RD|SEL_WR|SEL_ERR);
if (minor < 0 || minor >= NR_DEVS)
return ENXIO;
want_ops = ops & (SEL_RD|SEL_WR|SEL_ERR);
/* Read blocks when there is no log. */
if((m_ptr->DEV_SEL_OPS & SEL_RD) && logdevices[d].log_size > 0) {
if ((want_ops & SEL_RD) && logdevices[minor].log_size > 0) {
#if LOG_DEBUG
printf("log can read; size %d\n", logdevices[d].log_size);
printf("log can read; size %d\n", logdevices[minor].log_size);
#endif
ready_ops |= SEL_RD; /* writes never block */
ready_ops |= SEL_RD;
}
/* Write never blocks. */
if(m_ptr->DEV_SEL_OPS & SEL_WR) ready_ops |= SEL_WR;
if (want_ops & SEL_WR) ready_ops |= SEL_WR;
/* Enable select calback if no operations were
* ready to go, but operations were requested,
* and notify was enabled.
*/
if((m_ptr->DEV_SEL_OPS & SEL_NOTIFY) && ops && !ready_ops) {
logdevices[d].log_selected |= ops;
logdevices[d].log_select_proc = m_ptr->m_source;
/* Enable select calback if not all requested operations were ready to go,
* and notify was enabled.
*/
want_ops &= ~ready_ops;
if ((ops & SEL_NOTIFY) && want_ops) {
logdevices[minor].log_selected |= want_ops;
logdevices[minor].log_select_proc = endpt;
#if LOG_DEBUG
printf("log setting selector.\n");
#endif
@ -426,4 +350,3 @@ static int log_select(message *m_ptr)
return(ready_ops);
}

View file

@ -10,24 +10,18 @@
/* Constants and types. */
#define LOG_SIZE (50*1024)
#define SUSPENDABLE 1
struct logdevice {
char log_buffer[LOG_SIZE];
int log_size, /* no. of bytes in log buffer */
log_read, /* read mark */
log_write; /* write mark */
#if SUSPENDABLE
endpoint_t log_proc_nr,
log_source;
endpoint_t log_source;
cdev_id_t log_id;
int log_iosize,
log_revive_alerted,
log_status;
cp_grant_id_t log_user_grant;
vir_bytes log_user_offset;
#endif
int log_selected, log_select_proc,
log_select_alerted, log_select_ready_ops;
cp_grant_id_t log_grant;
int log_selected, log_select_proc;
};
/* Function prototypes. */

View file

@ -41,19 +41,18 @@
static struct device m_geom[NR_DEVS]; /* base and size of each device */
static vir_bytes m_vaddrs[NR_DEVS];
static dev_t m_device; /* current minor character device */
static int openct[NR_DEVS];
static struct device *m_prepare(dev_t device);
static int m_transfer(endpoint_t endpt, int opcode, u64_t position,
iovec_t *iov, unsigned int nr_req, endpoint_t user_endpt, unsigned int
flags);
static int m_do_open(message *m_ptr);
static int m_do_close(message *m_ptr);
static ssize_t m_char_read(devminor_t minor, u64_t position, endpoint_t endpt,
cp_grant_id_t grant, size_t size, int flags, cdev_id_t id);
static ssize_t m_char_write(devminor_t minor, u64_t position, endpoint_t endpt,
cp_grant_id_t grant, size_t size, int flags, cdev_id_t id);
static int m_char_open(devminor_t minor, int access, endpoint_t user_endpt);
static int m_char_close(devminor_t minor);
static struct device *m_block_part(devminor_t minor);
static int m_block_transfer(devminor_t minor, int do_write, u64_t position,
static ssize_t m_block_transfer(devminor_t minor, int do_write, u64_t position,
endpoint_t endpt, iovec_t *iov, unsigned int nr_req, int flags);
static int m_block_open(devminor_t minor, int access);
static int m_block_close(devminor_t minor);
@ -62,16 +61,10 @@ static int m_block_ioctl(devminor_t minor, unsigned long request, endpoint_t
/* Entry points to the CHARACTER part of this driver. */
static struct chardriver m_cdtab = {
m_do_open, /* open or mount */
m_do_close, /* nothing on a close */
nop_ioctl, /* no I/O control */
m_prepare, /* prepare for I/O on a given minor device */
m_transfer, /* do the I/O */
nop_cleanup, /* no need to clean up */
nop_alarm, /* no alarms */
nop_cancel, /* no blocking operations */
nop_select, /* select not supported */
NULL /* other messages not supported */
.cdr_open = m_char_open, /* open device */
.cdr_close = m_char_close, /* close device */
.cdr_read = m_char_read, /* read from device */
.cdr_write = m_char_write /* write to device */
};
/* Entry points to the BLOCK part of this driver. */
@ -84,14 +77,10 @@ static struct blockdriver m_bdtab = {
.bdr_part = m_block_part /* return partition information */
};
#define click_to_round_k(n) \
((unsigned) ((((unsigned long) (n) << CLICK_SHIFT) + 512) / 1024))
/* SEF functions and variables. */
static void sef_local_startup(void);
static int sef_cb_init_fresh(int type, sef_init_info_t *info);
/*===========================================================================*
* main *
*===========================================================================*/
@ -151,8 +140,8 @@ static int sef_cb_init_fresh(int UNUSED(type), sef_init_info_t *UNUSED(info))
}
/* Map in kernel memory for /dev/kmem. */
m_geom[KMEM_DEV].dv_base = cvul64(kinfo.kmem_base);
m_geom[KMEM_DEV].dv_size = cvul64(kinfo.kmem_size);
m_geom[KMEM_DEV].dv_base = kinfo.kmem_base;
m_geom[KMEM_DEV].dv_size = kinfo.kmem_size;
if((m_vaddrs[KMEM_DEV] = vm_map_phys(SELF, (void *) kinfo.kmem_base,
kinfo.kmem_size)) == MAP_FAILED) {
printf("MEM: Couldn't map in /dev/kmem.");
@ -160,16 +149,16 @@ static int sef_cb_init_fresh(int UNUSED(type), sef_init_info_t *UNUSED(info))
#endif
/* Ramdisk image built into the memory driver */
m_geom[IMGRD_DEV].dv_base= ((u64_t)(0));
m_geom[IMGRD_DEV].dv_size= ((u64_t)(imgrd_size));
m_geom[IMGRD_DEV].dv_base= 0;
m_geom[IMGRD_DEV].dv_size= imgrd_size;
m_vaddrs[IMGRD_DEV] = (vir_bytes) imgrd;
for(i = 0; i < NR_DEVS; i++)
openct[i] = 0;
/* Set up memory range for /dev/mem. */
m_geom[MEM_DEV].dv_base = ((u64_t)(0));
m_geom[MEM_DEV].dv_size = ((u64_t)(0xffffffff));
m_geom[MEM_DEV].dv_base = 0;
m_geom[MEM_DEV].dv_size = 0xffffffffULL;
m_vaddrs[MEM_DEV] = (vir_bytes) MAP_FAILED; /* we are not mapping this in. */
@ -196,206 +185,218 @@ static int m_is_block(devminor_t minor)
}
/*===========================================================================*
* m_prepare *
* m_transfer_kmem *
*===========================================================================*/
static struct device *m_prepare(dev_t device)
static ssize_t m_transfer_kmem(devminor_t minor, int do_write, u64_t position,
endpoint_t endpt, cp_grant_id_t grant, size_t size)
{
/* Prepare for I/O on a device: check if the minor device number is ok. */
if (device >= NR_DEVS || m_is_block(device)) return(NULL);
m_device = device;
return(&m_geom[device]);
}
/*===========================================================================*
* m_transfer *
*===========================================================================*/
static int m_transfer(
endpoint_t endpt, /* endpoint of grant owner */
int opcode, /* DEV_GATHER_S or DEV_SCATTER_S */
u64_t pos64, /* offset on device to read or write */
iovec_t *iov, /* pointer to read or write request vector */
unsigned int nr_req, /* length of request vector */
endpoint_t UNUSED(user_endpt),/* endpoint of user process */
unsigned int UNUSED(flags)
)
{
/* Read or write one the driver's character devices. */
unsigned count;
vir_bytes vir_offset = 0;
struct device *dv;
u64_t dv_size;
int s, r;
u64_t position;
cp_grant_id_t grant;
vir_bytes dev_vaddr;
/* ZERO_DEV and NULL_DEV are infinite in size. */
if (m_device != ZERO_DEV && m_device != NULL_DEV && ex64hi(pos64) != 0)
return OK; /* Beyond EOF */
position= pos64;
/* Get minor device number and check for /dev/null. */
dv = &m_geom[m_device];
dv_size = dv->dv_size;
dev_vaddr = m_vaddrs[m_device];
while (nr_req > 0) {
/* How much to transfer and where to / from. */
count = iov->iov_size;
grant = (cp_grant_id_t) iov->iov_addr;
switch (m_device) {
/* No copying; ignore request. */
case NULL_DEV:
if (opcode == DEV_GATHER_S) return(OK); /* always at EOF */
break;
/* Virtual copying. For kernel memory. */
default:
case KMEM_DEV:
if(!dev_vaddr || dev_vaddr == (vir_bytes) MAP_FAILED) {
printf("MEM: dev %d not initialized\n", m_device);
return EIO;
}
if (position >= dv_size) return(OK); /* check for EOF */
if (position + count > dv_size) count = dv_size - position;
if (opcode == DEV_GATHER_S) { /* copy actual data */
r=sys_safecopyto(endpt, grant, vir_offset,
dev_vaddr + position, count);
} else {
r=sys_safecopyfrom(endpt, grant, vir_offset,
dev_vaddr + position, count);
}
if(r != OK) {
panic("I/O copy failed: %d", r);
}
break;
/* Physical copying. Only used to access entire memory.
* Transfer one 'page window' at a time.
*/
case MEM_DEV:
{
u32_t pagestart, page_off;
static u32_t pagestart_mapped;
static int any_mapped = 0;
static char *vaddr;
int r;
u32_t subcount;
phys_bytes mem_phys;
if (position >= dv_size)
return(OK); /* check for EOF */
if (position + count > dv_size)
count = dv_size - position;
mem_phys = position;
page_off = mem_phys % PAGE_SIZE;
pagestart = mem_phys - page_off;
/* All memory to the map call has to be page-aligned.
* Don't have to map same page over and over.
*/
if(!any_mapped || pagestart_mapped != pagestart) {
if(any_mapped) {
if(vm_unmap_phys(SELF, vaddr, PAGE_SIZE) != OK)
panic("vm_unmap_phys failed");
any_mapped = 0;
}
vaddr = vm_map_phys(SELF, (void *) pagestart, PAGE_SIZE);
if(vaddr == MAP_FAILED)
r = ENOMEM;
else
r = OK;
if(r != OK) {
printf("memory: vm_map_phys failed\n");
return r;
}
any_mapped = 1;
pagestart_mapped = pagestart;
}
/* how much to be done within this page. */
subcount = PAGE_SIZE-page_off;
if(subcount > count)
subcount = count;
if (opcode == DEV_GATHER_S) { /* copy data */
s=sys_safecopyto(endpt, grant,
vir_offset, (vir_bytes) vaddr+page_off, subcount);
} else {
s=sys_safecopyfrom(endpt, grant,
vir_offset, (vir_bytes) vaddr+page_off, subcount);
}
if(s != OK)
return s;
count = subcount;
break;
}
/* Null byte stream generator. */
case ZERO_DEV:
if (opcode == DEV_GATHER_S)
if ((s = sys_safememset(endpt, grant, 0, '\0', count)) != OK)
return s;
break;
}
/* Book the number of bytes transferred. */
position += count;
vir_offset += count;
if ((iov->iov_size -= count) == 0) { iov++; nr_req--; vir_offset = 0; }
}
return(OK);
}
/*===========================================================================*
* m_do_open *
*===========================================================================*/
static int m_do_open(message *m_ptr)
{
/* Open a memory character device. */
/* Transfer from or to the KMEM device. */
u64_t dv_size, dev_vaddr;
int r;
/* Check device number on open. */
if (m_prepare(m_ptr->DEVICE) == NULL) return(ENXIO);
dv_size = m_geom[minor].dv_size;
dev_vaddr = m_vaddrs[minor];
if (!dev_vaddr || dev_vaddr == (vir_bytes) MAP_FAILED) {
printf("MEM: dev %d not initialized\n", minor);
return EIO;
}
if (position >= dv_size) return 0; /* check for EOF */
if (position + size > dv_size) size = dv_size - position;
if (!do_write) /* copy actual data */
r = sys_safecopyto(endpt, grant, 0, dev_vaddr + position, size);
else
r = sys_safecopyfrom(endpt, grant, 0, dev_vaddr + position, size);
return (r != OK) ? r : size;
}
/*===========================================================================*
* m_transfer_mem *
*===========================================================================*/
static ssize_t m_transfer_mem(devminor_t minor, int do_write, u64_t position,
endpoint_t endpt, cp_grant_id_t grant, size_t size)
{
/* Transfer from or to the MEM device. */
static int any_mapped = 0;
static phys_bytes pagestart_mapped;
static char *vaddr;
phys_bytes mem_phys, pagestart;
size_t off, page_off, subcount;
u64_t dv_size;
int r;
dv_size = m_geom[minor].dv_size;
if (position >= dv_size) return 0; /* check for EOF */
if (position + size > dv_size) size = dv_size - position;
/* Physical copying. Only used to access entire memory.
* Transfer one 'page window' at a time.
*/
off = 0;
while (off < size) {
mem_phys = (phys_bytes) position;
page_off = (size_t) (mem_phys % PAGE_SIZE);
pagestart = mem_phys - page_off;
/* All memory to the map call has to be page-aligned.
* Don't have to map same page over and over.
*/
if (!any_mapped || pagestart_mapped != pagestart) {
if (any_mapped) {
if (vm_unmap_phys(SELF, vaddr, PAGE_SIZE) != OK)
panic("vm_unmap_phys failed");
any_mapped = 0;
}
vaddr = vm_map_phys(SELF, (void *) pagestart, PAGE_SIZE);
if (vaddr == MAP_FAILED) {
printf("memory: vm_map_phys failed\n");
return ENOMEM;
}
any_mapped = 1;
pagestart_mapped = pagestart;
}
/* how much to be done within this page. */
subcount = PAGE_SIZE - page_off;
if (subcount > size)
subcount = size;
if (!do_write) /* copy data */
r = sys_safecopyto(endpt, grant, off,
(vir_bytes) vaddr + page_off, subcount);
else
r = sys_safecopyfrom(endpt, grant, off,
(vir_bytes) vaddr + page_off, subcount);
if (r != OK)
return r;
position += subcount;
off += subcount;
}
return off;
}
/*===========================================================================*
* m_char_read *
*===========================================================================*/
static ssize_t m_char_read(devminor_t minor, u64_t position, endpoint_t endpt,
cp_grant_id_t grant, size_t size, int UNUSED(flags),
cdev_id_t UNUSED(id))
{
/* Read from one of the driver's character devices. */
ssize_t r;
/* Check if the minor device number is ok. */
if (minor < 0 || minor >= NR_DEVS || m_is_block(minor)) return ENXIO;
switch (minor) {
case NULL_DEV:
r = 0; /* always at EOF */
break;
case ZERO_DEV:
/* Fill the target area with zeroes. In fact, let the kernel do it! */
if ((r = sys_safememset(endpt, grant, 0, '\0', size)) == OK)
r = size;
break;
case KMEM_DEV:
r = m_transfer_kmem(minor, FALSE, position, endpt, grant, size);
break;
case MEM_DEV:
r = m_transfer_mem(minor, FALSE, position, endpt, grant, size);
break;
default:
panic("unknown character device %d", minor);
}
return r;
}
/*===========================================================================*
* m_char_write *
*===========================================================================*/
static ssize_t m_char_write(devminor_t minor, u64_t position, endpoint_t endpt,
cp_grant_id_t grant, size_t size, int UNUSED(flags),
cdev_id_t UNUSED(id))
{
/* Write to one of the driver's character devices. */
ssize_t r;
/* Check if the minor device number is ok. */
if (minor < 0 || minor >= NR_DEVS || m_is_block(minor)) return ENXIO;
switch (minor) {
case NULL_DEV:
case ZERO_DEV:
r = size; /* just eat everything */
break;
case KMEM_DEV:
r = m_transfer_kmem(minor, TRUE, position, endpt, grant, size);
break;
case MEM_DEV:
r = m_transfer_mem(minor, TRUE, position, endpt, grant, size);
break;
default:
panic("unknown character device %d", minor);
}
return r;
}
/*===========================================================================*
* m_char_open *
*===========================================================================*/
static int m_char_open(devminor_t minor, int access, endpoint_t user_endpt)
{
/* Open a memory character device. */
/* Check if the minor device number is ok. */
if (minor < 0 || minor >= NR_DEVS || m_is_block(minor)) return ENXIO;
#if defined(__i386__)
if (m_device == MEM_DEV)
if (minor == MEM_DEV)
{
r = sys_enable_iop(m_ptr->USER_ENDPT);
int r = sys_enable_iop(user_endpt);
if (r != OK)
{
printf("m_do_open: sys_enable_iop failed for %d: %d\n",
m_ptr->USER_ENDPT, r);
printf("m_char_open: sys_enable_iop failed for %d: %d\n",
user_endpt, r);
return r;
}
}
#endif
openct[m_device]++;
openct[minor]++;
return(OK);
}
/*===========================================================================*
* m_do_close *
* m_char_close *
*===========================================================================*/
static int m_do_close(message *m_ptr)
static int m_char_close(devminor_t minor)
{
/* Close a memory character device. */
if (m_prepare(m_ptr->DEVICE) == NULL) return(ENXIO);
if(openct[m_device] < 1) {
printf("MEMORY: closing unopened device %d\n", m_device);
if (minor < 0 || minor >= NR_DEVS || m_is_block(minor)) return ENXIO;
if(openct[minor] < 1) {
printf("MEMORY: closing unopened device %d\n", minor);
return(EINVAL);
}
openct[m_device]--;
openct[minor]--;
return(OK);
}
@ -417,7 +418,7 @@ static struct device *m_block_part(devminor_t minor)
static int m_block_transfer(
devminor_t minor, /* minor device number */
int do_write, /* read or write? */
u64_t pos64, /* offset on device to read or write */
u64_t position, /* offset on device to read or write */
endpoint_t endpt, /* process doing the request */
iovec_t *iov, /* pointer to read or write request vector */
unsigned int nr_req, /* length of request vector */
@ -430,7 +431,6 @@ static int m_block_transfer(
struct device *dv;
u64_t dv_size;
int r;
u64_t position;
vir_bytes dev_vaddr;
cp_grant_id_t grant;
ssize_t total = 0;
@ -440,9 +440,8 @@ static int m_block_transfer(
dv_size = dv->dv_size;
dev_vaddr = m_vaddrs[minor];
if (ex64hi(pos64) != 0)
if (ex64hi(position) != 0)
return OK; /* Beyond EOF */
position= pos64;
while (nr_req > 0) {
@ -547,7 +546,7 @@ static int m_block_ioctl(devminor_t minor, unsigned long request,
return s;
if(is_imgrd)
ramdev_size = 0;
if(m_vaddrs[minor] && !cmp64(dv->dv_size, ((u64_t)(ramdev_size)))) {
if(m_vaddrs[minor] && dv->dv_size == (u64_t) ramdev_size) {
return(OK);
}
/* openct is 1 for the ioctl(). */
@ -595,7 +594,7 @@ static int m_block_ioctl(devminor_t minor, unsigned long request,
m_vaddrs[minor] = (vir_bytes) mem;
dv->dv_size = ((u64_t)(ramdev_size));
dv->dv_size = ramdev_size;
return(OK);
}

View file

@ -21,25 +21,20 @@ static dev_t m_device; /* current device */
extern int errno; /* error number for PM calls */
static struct device *r_prepare(dev_t device);
static int r_transfer(endpoint_t endpt, int opcode, u64_t position,
iovec_t *iov, unsigned int nr_req, endpoint_t user_endpt, unsigned int
flags);
static int r_do_open(message *m_ptr);
static void r_random(message *m_ptr);
static ssize_t r_read(devminor_t minor, u64_t position, endpoint_t endpt,
cp_grant_id_t grant, size_t size, int flags, cdev_id_t id);
static ssize_t r_write(devminor_t minor, u64_t position, endpoint_t endpt,
cp_grant_id_t grant, size_t size, int flags, cdev_id_t id);
static int r_open(devminor_t minor, int access, endpoint_t user_endpt);
static void r_random(clock_t stamp);
static void r_updatebin(int source, struct k_randomness_bin *rb);
/* Entry points to this driver. */
static struct chardriver r_dtab = {
r_do_open, /* open or mount */
do_nop, /* nothing on a close */
nop_ioctl, /* no I/O controls supported */
r_prepare, /* prepare for I/O on a given minor device */
r_transfer, /* do the I/O */
nop_cleanup, /* no need to clean up */
r_random, /* get randomness from kernel (alarm) */
nop_cancel, /* cancel not supported */
nop_select, /* select not supported */
NULL, /* other messages not supported */
.cdr_open = r_open, /* open device */
.cdr_read = r_read, /* read from device */
.cdr_write = r_write, /* write to device (seeding it) */
.cdr_alarm = r_random /* get randomness from kernel (alarm) */
};
/* Buffer for the /dev/random number generator. */
@ -92,7 +87,7 @@ static int sef_cb_init_fresh(int UNUSED(type), sef_init_info_t *UNUSED(info))
int i, s;
random_init();
r_random(NULL); /* also set periodic timer */
r_random(0); /* also set periodic timer */
/* Retrieve first randomness buffer with parameters. */
if (OK != (s=sys_getrandomness(&krandom))) {
@ -120,103 +115,71 @@ static int sef_cb_init_fresh(int UNUSED(type), sef_init_info_t *UNUSED(info))
}
/*===========================================================================*
* r_prepare *
* r_read *
*===========================================================================*/
static struct device *r_prepare(dev_t device)
static ssize_t r_read(devminor_t minor, u64_t UNUSED(position),
endpoint_t endpt, cp_grant_id_t grant, size_t size, int UNUSED(flags),
cdev_id_t UNUSED(id))
{
/* Prepare for I/O on a device: check if the minor device number is ok. */
if (device >= NR_DEVS) return(NULL);
m_device = device;
return(&m_geom[device]);
}
/*===========================================================================*
* r_transfer *
*===========================================================================*/
static int r_transfer(
endpoint_t endpt, /* endpoint of grant owner */
int opcode, /* DEV_GATHER or DEV_SCATTER */
u64_t position, /* offset on device to read or write */
iovec_t *iov, /* pointer to read or write request vector */
unsigned int nr_req, /* length of request vector */
endpoint_t UNUSED(user_endpt),/* endpoint of user process */
unsigned int UNUSED(flags)
)
{
/* Read or write one the driver's minor devices. */
unsigned count, left, chunk;
cp_grant_id_t grant;
struct device *dv;
/* Read from one of the driver's minor devices. */
size_t offset, chunk;
int r;
size_t vir_offset = 0;
/* Get minor device number and check for /dev/null. */
dv = &m_geom[m_device];
if (minor != RANDOM_DEV) return(EIO);
while (nr_req > 0) {
if (!random_isseeded()) return(EAGAIN);
/* How much to transfer and where to / from. */
count = iov->iov_size;
grant = (cp_grant_id_t) iov->iov_addr;
switch (m_device) {
/* Random number generator. Character instead of block device. */
case RANDOM_DEV:
if (opcode == DEV_GATHER_S && !random_isseeded())
return(EAGAIN);
left = count;
while (left > 0) {
chunk = (left > RANDOM_BUF_SIZE) ? RANDOM_BUF_SIZE : left;
if (opcode == DEV_GATHER_S) {
random_getbytes(random_buf, chunk);
r= sys_safecopyto(endpt, grant, vir_offset,
(vir_bytes) random_buf, chunk);
if (r != OK)
{
printf("random: sys_safecopyto failed for proc %d, "
"grant %d\n", endpt, grant);
return r;
}
} else if (opcode == DEV_SCATTER_S) {
r= sys_safecopyfrom(endpt, grant, vir_offset,
(vir_bytes) random_buf, chunk);
if (r != OK)
{
printf("random: sys_safecopyfrom failed for proc %d, "
"grant %d\n", endpt, grant);
return r;
}
random_putbytes(random_buf, chunk);
}
vir_offset += chunk;
left -= chunk;
}
break;
/* Unknown (illegal) minor device. */
default:
return(EINVAL);
for (offset = 0; offset < size; offset += chunk) {
chunk = MIN(size - offset, RANDOM_BUF_SIZE);
random_getbytes(random_buf, chunk);
r = sys_safecopyto(endpt, grant, offset, (vir_bytes)random_buf, chunk);
if (r != OK) {
printf("random: sys_safecopyto failed for proc %d, grant %d\n",
endpt, grant);
return r;
}
/* Book the number of bytes transferred. */
position += count;
if ((iov->iov_size -= count) == 0) { iov++; nr_req--; vir_offset = 0; }
}
return(OK);
return size;
}
/*===========================================================================*
* r_do_open *
* r_write *
*===========================================================================*/
static int r_do_open(message *m_ptr)
static ssize_t r_write(devminor_t minor, u64_t UNUSED(position),
endpoint_t endpt, cp_grant_id_t grant, size_t size, int UNUSED(flags),
cdev_id_t UNUSED(id))
{
/* Write to one of the driver's minor devices. */
size_t offset, chunk;
int r;
if (minor != RANDOM_DEV) return(EIO);
for (offset = 0; offset < size; offset += chunk) {
chunk = MIN(size - offset, RANDOM_BUF_SIZE);
r = sys_safecopyfrom(endpt, grant, offset, (vir_bytes)random_buf,
chunk);
if (r != OK) {
printf("random: sys_safecopyfrom failed for proc %d,"
" grant %d\n", endpt, grant);
return r;
}
random_putbytes(random_buf, chunk);
}
return size;
}
/*===========================================================================*
* r_open *
*===========================================================================*/
static int r_open(devminor_t minor, int access, endpoint_t UNUSED(user_endpt))
{
/* Check device number on open.
*/
if (r_prepare(m_ptr->DEVICE) == NULL) return(ENXIO);
if (minor < 0 || minor >= NR_DEVS) return(ENXIO);
return(OK);
}
@ -265,7 +228,7 @@ static void r_updatebin(int source, struct k_randomness_bin *rb)
/*===========================================================================*
* r_random *
*===========================================================================*/
static void r_random(message *UNUSED(m_ptr))
static void r_random(clock_t UNUSED(stamp))
{
/* Fetch random information from the kernel to update /dev/random. */
int s;

View file

@ -114,11 +114,9 @@ static uint8_t crc8(uint8_t crc, uint8_t byte);
static int checksum(uint8_t * bytes, int nbytes, uint8_t expected_crc);
/* libchardriver callbacks */
static struct device *sht21_prepare(dev_t UNUSED(dev));
static int sht21_transfer(endpoint_t endpt, int opcode, u64_t position,
iovec_t * iov, unsigned nr_req, endpoint_t UNUSED(user_endpt),
unsigned int UNUSED(flags));
static int sht21_other(message * m);
static ssize_t sht21_read(devminor_t minor, u64_t position, endpoint_t endpt,
cp_grant_id_t grant, size_t size, int flags, cdev_id_t id);
static void sht21_other(message * m, int ipc_status);
/* SEF functions */
static int sef_cb_lu_state_save(int);
@ -128,21 +126,8 @@ static void sef_local_startup(void);
/* Entry points to this driver from libchardriver. */
static struct chardriver sht21_tab = {
.cdr_open = do_nop,
.cdr_close = do_nop,
.cdr_ioctl = nop_ioctl,
.cdr_prepare = sht21_prepare,
.cdr_transfer = sht21_transfer,
.cdr_cleanup = nop_cleanup,
.cdr_alarm = nop_alarm,
.cdr_cancel = nop_cancel,
.cdr_select = nop_select,
.cdr_other = sht21_other
};
static struct device sht21_device = {
.dv_base = 0,
.dv_size = 0
.cdr_read = sht21_read,
.cdr_other = sht21_other
};
/*
@ -342,17 +327,11 @@ checksum(uint8_t * bytes, int nbytes, uint8_t expected_crc)
}
}
static struct device *
sht21_prepare(dev_t UNUSED(dev))
{
return &sht21_device;
}
static int
sht21_transfer(endpoint_t endpt, int opcode, u64_t position,
iovec_t * iov, unsigned nr_req, endpoint_t UNUSED(user_endpt),
unsigned int UNUSED(flags))
static ssize_t
sht21_read(devminor_t UNUSED(minor), u64_t position, endpoint_t endpt,
cp_grant_id_t grant, size_t size, int UNUSED(flags), cdev_id_t UNUSED(id))
{
u64_t dev_size;
int bytes, r;
r = measure();
@ -367,48 +346,33 @@ sht21_transfer(endpoint_t endpt, int opcode, u64_t position,
log_trace(&log, "%s", buffer);
bytes = strlen(buffer) - position < iov->iov_size ?
strlen(buffer) - position : iov->iov_size;
dev_size = (u64_t)strlen(buffer);
if (position >= dev_size) return 0;
if (position + size > dev_size)
size = (size_t)(dev_size - position);
if (bytes <= 0) {
return OK;
}
r = sys_safecopyto(endpt, grant, 0,
(vir_bytes)(buffer + (size_t)position), size);
switch (opcode) {
case DEV_GATHER_S:
r = sys_safecopyto(endpt, (cp_grant_id_t) iov->iov_addr, 0,
(vir_bytes) (buffer + position), bytes);
iov->iov_size -= bytes;
break;
default:
return EINVAL;
}
return r;
return (r != OK) ? r : size;
}
static int
sht21_other(message * m)
static void
sht21_other(message * m, int ipc_status)
{
int r;
switch (m->m_type) {
case NOTIFY_MESSAGE:
if (is_ipc_notify(ipc_status)) {
if (m->m_source == DS_PROC_NR) {
log_debug(&log,
"bus driver changed state, update endpoint\n");
i2cdriver_handle_bus_update(&bus_endpoint, bus,
address);
}
r = OK;
break;
default:
log_warn(&log, "Invalid message type (0x%x)\n", m->m_type);
r = EINVAL;
break;
return;
}
return r;
log_warn(&log, "Invalid message type (0x%x)\n", m->m_type);
}
static int

View file

@ -65,11 +65,9 @@ static int adc_read(int adc, uint8_t * val);
static int measure_lux(uint32_t * lux);
/* libchardriver callbacks */
static struct device *tsl2550_prepare(dev_t UNUSED(dev));
static int tsl2550_transfer(endpoint_t endpt, int opcode, u64_t position,
iovec_t * iov, unsigned nr_req, endpoint_t UNUSED(user_endpt),
unsigned int UNUSED(flags));
static int tsl2550_other(message * m);
static ssize_t tsl2550_read(devminor_t minor, u64_t position, endpoint_t endpt,
cp_grant_id_t grant, size_t size, int flags, cdev_id_t id);
static void tsl2550_other(message * m, int ipc_status);
/* SEF functions */
static int sef_cb_lu_state_save(int);
@ -79,21 +77,8 @@ static void sef_local_startup(void);
/* Entry points to this driver from libchardriver. */
static struct chardriver tsl2550_tab = {
.cdr_open = do_nop,
.cdr_close = do_nop,
.cdr_ioctl = nop_ioctl,
.cdr_prepare = tsl2550_prepare,
.cdr_transfer = tsl2550_transfer,
.cdr_cleanup = nop_cleanup,
.cdr_alarm = nop_alarm,
.cdr_cancel = nop_cancel,
.cdr_select = nop_select,
.cdr_other = tsl2550_other
};
static struct device tsl2550_device = {
.dv_base = 0,
.dv_size = 0
.cdr_read = tsl2550_read,
.cdr_other = tsl2550_other
};
/*
@ -294,17 +279,11 @@ tsl2550_init(void)
return OK;
}
static struct device *
tsl2550_prepare(dev_t UNUSED(dev))
{
return &tsl2550_device;
}
static int
tsl2550_transfer(endpoint_t endpt, int opcode, u64_t position,
iovec_t * iov, unsigned nr_req, endpoint_t UNUSED(user_endpt),
unsigned int UNUSED(flags))
static ssize_t
tsl2550_read(devminor_t UNUSED(minor), u64_t position, endpoint_t endpt,
cp_grant_id_t grant, size_t size, int UNUSED(flags), cdev_id_t UNUSED(id))
{
u64_t dev_size;
int bytes, r;
uint32_t lux;
@ -316,48 +295,33 @@ tsl2550_transfer(endpoint_t endpt, int opcode, u64_t position,
memset(buffer, '\0', BUFFER_LEN + 1);
snprintf(buffer, BUFFER_LEN, "%-16s: %d\n", "ILLUMINANCE", lux);
bytes = strlen(buffer) - position < iov->iov_size ?
strlen(buffer) - position : iov->iov_size;
dev_size = (u64_t)strlen(buffer);
if (position >= dev_size) return 0;
if (position + size > dev_size)
size = (size_t)(dev_size - position);
if (bytes <= 0) {
return OK;
}
r = sys_safecopyto(endpt, grant, 0,
(vir_bytes)(buffer + (size_t)position), size);
switch (opcode) {
case DEV_GATHER_S:
r = sys_safecopyto(endpt, (cp_grant_id_t) iov->iov_addr, 0,
(vir_bytes) (buffer + position), bytes);
iov->iov_size -= bytes;
break;
default:
return EINVAL;
}
return r;
return (r != OK) ? r : size;
}
static int
tsl2550_other(message * m)
static void
tsl2550_other(message * m, int ipc_status)
{
int r;
switch (m->m_type) {
case NOTIFY_MESSAGE:
if (is_ipc_notify(ipc_status)) {
if (m->m_source == DS_PROC_NR) {
log_debug(&log,
"bus driver changed state, update endpoint\n");
i2cdriver_handle_bus_update(&bus_endpoint, bus,
address);
}
r = OK;
break;
default:
log_warn(&log, "Invalid message type (0x%x)\n", m->m_type);
r = EINVAL;
break;
return;
}
return r;
log_warn(&log, "Invalid message type (0x%x)\n", m->m_type);
}
static int

View file

@ -3,20 +3,23 @@
#include <minix/driver.h>
typedef unsigned int cdev_id_t;
/* Entry points into the device dependent code of character drivers. */
struct chardriver {
int(*cdr_open) (message *m_ptr);
int(*cdr_close) (message *m_ptr);
int(*cdr_ioctl) (message *m_ptr);
struct device *(*cdr_prepare)(dev_t device);
int(*cdr_transfer) (endpoint_t endpt, int opcode, u64_t position,
iovec_t *iov, unsigned int nr_req, endpoint_t user_endpt, unsigned int
flags);
void(*cdr_cleanup) (void);
void(*cdr_alarm) (message *m_ptr);
int(*cdr_cancel) (message *m_ptr);
int(*cdr_select) (message *m_ptr);
int(*cdr_other) (message *m_ptr);
int (*cdr_open)(devminor_t minor, int access, endpoint_t user_endpt);
int (*cdr_close)(devminor_t minor);
ssize_t (*cdr_read)(devminor_t minor, u64_t position, endpoint_t endpt,
cp_grant_id_t grant, size_t size, int flags, cdev_id_t id);
ssize_t (*cdr_write)(devminor_t minor, u64_t position, endpoint_t endpt,
cp_grant_id_t grant, size_t size, int flags, cdev_id_t id);
int (*cdr_ioctl)(devminor_t minor, unsigned long request, endpoint_t endpt,
cp_grant_id_t grant, int flags, endpoint_t user_endpt, cdev_id_t id);
int (*cdr_cancel)(devminor_t minor, endpoint_t endpt, cdev_id_t id);
int (*cdr_select)(devminor_t minor, unsigned int ops, endpoint_t endpt);
void (*cdr_intr)(unsigned int mask);
void (*cdr_alarm)(clock_t stamp);
void (*cdr_other)(message *m_ptr, int ipc_status);
};
/* Functions defined by libchardriver. */
@ -26,11 +29,7 @@ void chardriver_process(struct chardriver *cdp, message *m_ptr,
void chardriver_terminate(void);
void chardriver_task(struct chardriver *cdp);
int do_nop(message *m_ptr);
void nop_cleanup(void);
void nop_alarm(message *m_ptr);
int nop_cancel(message *m_ptr);
int nop_select(message *m_ptr);
int nop_ioctl(message *m_ptr);
void chardriver_reply_task(endpoint_t endpt, cdev_id_t id, int r);
void chardriver_reply_select(endpoint_t endpt, devminor_t minor, int ops);
#endif /* _MINIX_CHARDRIVER_H */

View file

@ -23,12 +23,9 @@
* | DEV_SELECT | device | ops | | | | |
* ----------------------------------------------------------------------------
*
* The entry points into this file are:
* driver_task: the main message loop of the driver
* driver_receive: message receive interface for drivers
*
* Changes:
* Oct 20, 2013 retire synchronous protocol (D.C. van Moolenbroek)
* Sep 01, 2013 complete rewrite of the API (D.C. van Moolenboek)
* Aug 20, 2013 retire synchronous protocol (D.C. van Moolenbroek)
* Oct 16, 2011 split character and block protocol (D.C. van Moolenbroek)
* Aug 27, 2011 move common functions into driver.c (A. Welzel)
* Jul 25, 2005 added SYS_SIG type for signals (Jorrit N. Herder)
@ -44,7 +41,7 @@
static int running;
/* Management data for opened devices. */
static int open_devs[MAX_NR_OPEN_DEVICES];
static devminor_t open_devs[MAX_NR_OPEN_DEVICES];
static int next_open_devs_slot = 0;
/*===========================================================================*
@ -59,13 +56,13 @@ static void clear_open_devs(void)
/*===========================================================================*
* is_open_dev *
*===========================================================================*/
static int is_open_dev(int device)
static int is_open_dev(devminor_t minor)
{
/* Check whether the given minor device has previously been opened. */
int i;
for (i = 0; i < next_open_devs_slot; i++)
if (open_devs[i] == device)
if (open_devs[i] == minor)
return TRUE;
return FALSE;
@ -74,14 +71,14 @@ static int is_open_dev(int device)
/*===========================================================================*
* set_open_dev *
*===========================================================================*/
static void set_open_dev(int device)
static void set_open_dev(devminor_t minor)
{
/* Mark the given minor device as having been opened. */
if (next_open_devs_slot >= MAX_NR_OPEN_DEVICES)
panic("out of slots for open devices");
open_devs[next_open_devs_slot] = device;
open_devs[next_open_devs_slot] = minor;
next_open_devs_slot++;
}
@ -117,16 +114,103 @@ void chardriver_announce(void)
clear_open_devs();
}
/*===========================================================================*
* chardriver_reply_task *
*===========================================================================*/
void chardriver_reply_task(endpoint_t endpt, cdev_id_t id, int r)
{
/* Reply to a (read, write, ioctl) task request that was suspended earlier.
* Not-so-well-written drivers may use this function to send a reply to a
* request that is being processed right now, and then return EDONTREPLY later.
*/
message m_reply;
if (r == EDONTREPLY || r == SUSPEND)
panic("chardriver: bad task reply: %d", r);
memset(&m_reply, 0, sizeof(m_reply));
m_reply.m_type = DEV_REVIVE;
m_reply.REP_STATUS = r;
m_reply.REP_ENDPT = endpt; /* XXX FIXME: hack */
m_reply.REP_IO_GRANT = (cp_grant_id_t) id;
if ((r = asynsend3(endpt, &m_reply, AMF_NOREPLY)) != OK)
printf("chardriver_reply_task: send to %d failed: %d\n", endpt, r);
}
/*===========================================================================*
* chardriver_reply_select *
*===========================================================================*/
void chardriver_reply_select(endpoint_t endpt, devminor_t minor, int r)
{
/* Reply to a select request with a status update. This must not be used to
* reply to a select request that is being processed right now.
*/
message m_reply;
/* Replying with an error is allowed (if unusual). */
if (r == EDONTREPLY || r == SUSPEND)
panic("chardriver: bad select reply: %d", r);
memset(&m_reply, 0, sizeof(m_reply));
m_reply.m_type = DEV_SEL_REPL2;
m_reply.DEV_MINOR = minor;
m_reply.DEV_SEL_OPS = r;
if ((r = asynsend3(endpt, &m_reply, AMF_NOREPLY)) != OK)
printf("chardriver_reply_select: send to %d failed: %d\n", endpt, r);
}
/*===========================================================================*
* send_reply *
*===========================================================================*/
static void send_reply(message *mess, int ipc_status, int r)
static void send_reply(endpoint_t endpt, message *m_ptr, int ipc_status)
{
/* Send a reply message to a request. */
int r;
/* If we would block sending the message, send it asynchronously. */
if (IPC_STATUS_CALL(ipc_status) == SENDREC)
r = sendnb(endpt, m_ptr);
else
r = asynsend3(endpt, m_ptr, AMF_NOREPLY);
if (r != OK)
printf("chardriver: unable to send reply to %d: %d\n", endpt, r);
}
/*===========================================================================*
* chardriver_reply *
*===========================================================================*/
static void chardriver_reply(message *mess, int ipc_status, int r)
{
/* Prepare and send a reply message. */
message reply_mess;
if (r == EDONTREPLY)
return;
/* If the EDONTREPLY pseudo-reply is given, we do not reply. This is however
* allowed only for blocking task calls. Perform a sanity check.
*/
if (r == EDONTREPLY) {
switch (mess->m_type) {
case DEV_READ_S:
case DEV_WRITE_S:
case DEV_IOCTL_S:
#if 0 /* XXX doesn't match lwip's model, disabled for now */
if (mess->FLAGS & FLG_OP_NONBLOCK)
panic("chardriver: cannot suspend nonblocking I/O");
#endif
/*fall-through*/
case CANCEL:
return; /* alright */
default:
panic("chardriver: cannot suspend request %d", mess->m_type);
}
}
if (r == SUSPEND)
panic("chardriver: SUSPEND should not be used anymore");
/* Do not reply with ERESTART. The only possible caller, VFS, will find out
* through other means when we have restarted, and is not (fully) ready to
@ -144,6 +228,12 @@ static void send_reply(message *mess, int ipc_status, int r)
reply_mess.REP_STATUS = r;
break;
case DEV_REOPEN:
reply_mess.m_type = DEV_REOPEN_REPL;
reply_mess.REP_ENDPT = mess->USER_ENDPT;
reply_mess.REP_STATUS = r;
break;
case DEV_CLOSE:
reply_mess.m_type = DEV_CLOSE_REPL;
reply_mess.REP_ENDPT = mess->USER_ENDPT;
@ -153,20 +243,13 @@ static void send_reply(message *mess, int ipc_status, int r)
case DEV_READ_S:
case DEV_WRITE_S:
case DEV_IOCTL_S:
if (r == SUSPEND)
printf("chardriver_task: reviving %d (%d) with SUSPEND\n",
mess->m_source, mess->USER_ENDPT);
case CANCEL: /* For CANCEL, this is a reply to the original request! */
reply_mess.m_type = DEV_REVIVE;
reply_mess.REP_ENDPT = mess->USER_ENDPT;
reply_mess.REP_IO_GRANT = (cp_grant_id_t) mess->IO_GRANT;
reply_mess.REP_STATUS = r;
break;
case CANCEL:
/* The original request should send a reply. */
return;
case DEV_SELECT:
reply_mess.m_type = DEV_SEL_REPL1;
reply_mess.DEV_MINOR = mess->DEVICE;
@ -174,193 +257,266 @@ static void send_reply(message *mess, int ipc_status, int r)
break;
default:
reply_mess.m_type = DEV_REVIVE;
reply_mess.REP_ENDPT = mess->USER_ENDPT;
/* Status is # of bytes transferred or error code. */
reply_mess.REP_STATUS = r;
break;
panic("chardriver: unknown request %d", mess->m_type);
}
/* If we would block sending the message, send it asynchronously. */
if (IPC_STATUS_CALL(ipc_status) == SENDREC)
r = sendnb(mess->m_source, &reply_mess);
else
r = asynsend3(mess->m_source, &reply_mess, AMF_NOREPLY);
if (r != OK)
printf("send_reply: unable to send reply to %d: %d\n",
mess->m_source, r);
send_reply(mess->m_source, &reply_mess, ipc_status);
}
/*===========================================================================*
* do_rdwt *
* do_open *
*===========================================================================*/
static int do_rdwt(struct chardriver *cdp, message *mp)
static int do_open(struct chardriver *cdp, message *m_ptr, int is_reopen)
{
/* Carry out a single read or write request. */
iovec_t iovec1;
int r, opcode;
u64_t position;
/* Open a minor device. */
endpoint_t user_endpt;
devminor_t minor;
int r, access;
/* Disk address? Address and length of the user buffer? */
if (mp->COUNT < 0) return(EINVAL);
/* Default action if no open hook is in place. */
if (cdp->cdr_open == NULL)
return OK;
/* Prepare for I/O. */
if ((*cdp->cdr_prepare)(mp->DEVICE) == NULL) return(ENXIO);
/* Call the open hook. */
minor = m_ptr->DEVICE;
access = m_ptr->COUNT;
user_endpt = is_reopen ? NONE : m_ptr->USER_ENDPT; /* XXX FIXME */
/* Create a one element scatter/gather vector for the buffer. */
if(mp->m_type == DEV_READ_S)
opcode = DEV_GATHER_S;
else
opcode = DEV_SCATTER_S;
r = cdp->cdr_open(minor, access, user_endpt);
iovec1.iov_addr = (vir_bytes) mp->IO_GRANT;
iovec1.iov_size = mp->COUNT;
/* Transfer bytes from/to the device. */
position= make64(mp->POSITION, mp->HIGHPOS);
r = (*cdp->cdr_transfer)(mp->m_source, opcode, position, &iovec1, 1,
mp->USER_ENDPT, mp->FLAGS);
/* Return the number of bytes transferred or an error code. */
return(r == OK ? (int) (mp->COUNT - iovec1.iov_size) : r);
}
/*===========================================================================*
* do_vrdwt *
*===========================================================================*/
static int do_vrdwt(struct chardriver *cdp, message *mp)
{
/* Carry out an device read or write to/from a vector of user addresses.
* The "user addresses" are assumed to be safe, i.e. FS transferring to/from
* its own buffers, so they are not checked.
*/
iovec_t iovec[NR_IOREQS];
phys_bytes iovec_size;
unsigned nr_req;
int r, opcode;
u64_t position;
nr_req = mp->COUNT; /* Length of I/O vector */
/* Copy the vector from the caller to kernel space. */
if (nr_req > NR_IOREQS) nr_req = NR_IOREQS;
iovec_size = (phys_bytes) (nr_req * sizeof(iovec[0]));
if (OK != sys_safecopyfrom(mp->m_source, (vir_bytes) mp->IO_GRANT,
0, (vir_bytes) iovec, iovec_size)) {
printf("bad I/O vector by: %d\n", mp->m_source);
return(EINVAL);
}
/* Prepare for I/O. */
if ((*cdp->cdr_prepare)(mp->DEVICE) == NULL) return(ENXIO);
/* Transfer bytes from/to the device. */
opcode = mp->m_type;
position= make64(mp->POSITION, mp->HIGHPOS);
r = (*cdp->cdr_transfer)(mp->m_source, opcode, position, iovec, nr_req,
mp->USER_ENDPT, mp->FLAGS);
/* Copy the I/O vector back to the caller. */
if (OK != sys_safecopyto(mp->m_source, (vir_bytes) mp->IO_GRANT,
0, (vir_bytes) iovec, iovec_size)) {
printf("couldn't return I/O vector: %d\n", mp->m_source);
return(EINVAL);
}
return(r);
}
/*===========================================================================*
* handle_notify *
*===========================================================================*/
static void handle_notify(struct chardriver *cdp, message *m_ptr)
{
/* Take care of the notifications (interrupt and clock messages) by calling the
* appropiate callback functions. Never send a reply.
*/
/* Call the appropriate function for this notification. */
switch (_ENDPOINT_P(m_ptr->m_source)) {
case CLOCK:
if (cdp->cdr_alarm)
cdp->cdr_alarm(m_ptr);
break;
default:
if (cdp->cdr_other)
(void) cdp->cdr_other(m_ptr);
}
}
/*===========================================================================*
* handle_request *
*===========================================================================*/
static int handle_request(struct chardriver *cdp, message *m_ptr)
{
/* Call the appropiate driver function, based on the type of request. Return
* the result code that is to be sent back to the caller, or EDONTREPLY if no
* reply is to be sent.
*/
int r;
/* We might get spurious requests if the driver has been restarted. Deny any
* requests on devices that have not previously been opened, signaling the
* caller that something went wrong.
*/
if (IS_DEV_RQ(m_ptr->m_type) && !is_open_dev(m_ptr->DEVICE)) {
/* Reply ERESTART to spurious requests for unopened devices. */
if (m_ptr->m_type != DEV_OPEN)
return ERESTART;
/* Mark the device as opened otherwise. */
set_open_dev(m_ptr->DEVICE);
}
/* Call the appropriate function(s) for this request. */
switch (m_ptr->m_type) {
case DEV_OPEN: r = (*cdp->cdr_open)(m_ptr); break;
case DEV_CLOSE: r = (*cdp->cdr_close)(m_ptr); break;
case DEV_IOCTL_S: r = (*cdp->cdr_ioctl)(m_ptr); break;
case CANCEL: r = (*cdp->cdr_cancel)(m_ptr); break;
case DEV_SELECT: r = (*cdp->cdr_select)(m_ptr); break;
case DEV_READ_S:
case DEV_WRITE_S: r = do_rdwt(cdp, m_ptr); break;
case DEV_GATHER_S:
case DEV_SCATTER_S: r = do_vrdwt(cdp, m_ptr); break;
default:
if (cdp->cdr_other)
r = cdp->cdr_other(m_ptr);
else
r = EINVAL;
}
/* Let the driver perform any cleanup. */
if (cdp->cdr_cleanup)
(*cdp->cdr_cleanup)();
/* If the device has been cloned, mark the new minor as open too. */
if (r >= 0 && !is_open_dev(r)) /* XXX FIXME */
set_open_dev(r);
return r;
}
/*===========================================================================*
* do_close *
*===========================================================================*/
static int do_close(struct chardriver *cdp, message *m_ptr)
{
/* Close a minor device. */
devminor_t minor;
/* Default action if no close hook is in place. */
if (cdp->cdr_close == NULL)
return OK;
/* Call the close hook. */
minor = m_ptr->DEVICE;
return cdp->cdr_close(minor);
}
/*===========================================================================*
* do_trasnfer *
*===========================================================================*/
static int do_transfer(struct chardriver *cdp, message *m_ptr, int do_write)
{
/* Carry out a read or write task request. */
devminor_t minor;
u64_t position;
endpoint_t endpt;
cp_grant_id_t grant;
size_t size;
int flags;
cdev_id_t id;
ssize_t r;
minor = m_ptr->DEVICE;
position = make64(m_ptr->POSITION, m_ptr->HIGHPOS);
endpt = m_ptr->m_source;
grant = (cp_grant_id_t) m_ptr->IO_GRANT;
size = m_ptr->COUNT;
flags = m_ptr->FLAGS;
id = (cdev_id_t) m_ptr->IO_GRANT;
/* Call the read/write hook, if the appropriate one is in place. */
if (!do_write && cdp->cdr_read != NULL)
r = cdp->cdr_read(minor, position, endpt, grant, size, flags, id);
else if (do_write && cdp->cdr_write != NULL)
r = cdp->cdr_write(minor, position, endpt, grant, size, flags, id);
else
r = EIO; /* Default action if no read/write hook is in place. */
return r;
}
/*===========================================================================*
* do_ioctl *
*===========================================================================*/
static int do_ioctl(struct chardriver *cdp, message *m_ptr)
{
/* Carry out an I/O control task request. */
devminor_t minor;
unsigned long request;
cp_grant_id_t grant;
endpoint_t endpt, user_endpt;
int flags;
cdev_id_t id;
/* Default action if no ioctl hook is in place. */
if (cdp->cdr_ioctl == NULL)
return ENOTTY;
/* Call the ioctl hook. */
minor = m_ptr->DEVICE;
request = m_ptr->REQUEST;
endpt = m_ptr->m_source;
grant = (cp_grant_id_t) m_ptr->IO_GRANT;
flags = m_ptr->FLAGS;
user_endpt = (endpoint_t) m_ptr->POSITION;
id = (cdev_id_t) m_ptr->IO_GRANT;
return cdp->cdr_ioctl(minor, request, endpt, grant, flags, user_endpt, id);
}
/*===========================================================================*
* do_cancel *
*===========================================================================*/
static int do_cancel(struct chardriver *cdp, message *m_ptr)
{
/* Cancel a suspended (read, write, ioctl) task request. The original request
* may already have finished, in which case no reply should be sent.
*/
devminor_t minor;
endpoint_t endpt;
cdev_id_t id;
/* Default action if no cancel hook is in place: let the request finish. */
if (cdp->cdr_cancel == NULL)
return EDONTREPLY;
/* Call the cancel hook. */
minor = m_ptr->DEVICE;
endpt = m_ptr->m_source;
id = (cdev_id_t) m_ptr->IO_GRANT;
return cdp->cdr_cancel(minor, endpt, id);
}
/*===========================================================================*
* do_select *
*===========================================================================*/
static int do_select(struct chardriver *cdp, message *m_ptr)
{
/* Perform a select query on a minor device. */
devminor_t minor;
unsigned int ops;
endpoint_t endpt;
/* Default action if no select hook is in place. */
if (cdp->cdr_select == NULL)
return EBADF;
/* Call the select hook. */
minor = m_ptr->DEV_MINOR;
ops = m_ptr->DEV_SEL_OPS;
endpt = m_ptr->m_source;
return cdp->cdr_select(minor, ops, endpt);
}
/*===========================================================================*
* do_block_open *
*===========================================================================*/
static void do_block_open(message *m_ptr, int ipc_status)
{
/* Reply to a block driver open request stating there is no such device. */
message m_reply;
memset(&m_reply, 0, sizeof(m_reply));
m_reply.m_type = BDEV_REPLY;
m_reply.BDEV_STATUS = ENXIO;
m_reply.BDEV_ID = m_ptr->BDEV_ID;
send_reply(m_ptr->m_source, &m_reply, ipc_status);
}
/*===========================================================================*
* chardriver_process *
*===========================================================================*/
void chardriver_process(struct chardriver *cdp, message *m_ptr, int ipc_status)
{
/* Handle the given received message. */
int r;
/* Call the appropiate driver function, based on the type of request. Send a
* reply to the caller if necessary.
*/
int r, reply;
/* Process the notification or request. */
/* Check for notifications first. We never reply to notifications. */
if (is_ipc_notify(ipc_status)) {
handle_notify(cdp, m_ptr);
switch (_ENDPOINT_P(m_ptr->m_source)) {
case HARDWARE:
if (cdp->cdr_intr)
cdp->cdr_intr(m_ptr->NOTIFY_ARG);
break;
/* Do not reply to notifications. */
} else {
r = handle_request(cdp, m_ptr);
case CLOCK:
if (cdp->cdr_alarm)
cdp->cdr_alarm(m_ptr->NOTIFY_TIMESTAMP);
break;
send_reply(m_ptr, ipc_status, r);
default:
if (cdp->cdr_other)
cdp->cdr_other(m_ptr, ipc_status);
}
return; /* do not send a reply */
}
/* Reply to block driver open requests with an error code. Otherwise, if
* someone creates a block device node for a character driver, opening that
* device node will cause the corresponding VFS thread to block forever.
*/
if (m_ptr->m_type == BDEV_OPEN) {
do_block_open(m_ptr, ipc_status);
return;
}
/* We might get spurious requests if the driver has been restarted. Deny any
* requests on devices that have not previously been opened.
*/
if (IS_DEV_RQ(m_ptr->m_type) && !is_open_dev(m_ptr->DEVICE)) {
/* Ignore spurious requests for unopened devices. */
if (m_ptr->m_type != DEV_OPEN && m_ptr->m_type != DEV_REOPEN)
return; /* do not send a reply */
/* Mark the device as opened otherwise. */
set_open_dev(m_ptr->DEVICE);
}
/* Call the appropriate function(s) for this request. */
switch (m_ptr->m_type) {
case DEV_OPEN: r = do_open(cdp, m_ptr, FALSE); break;
case DEV_REOPEN: r = do_open(cdp, m_ptr, TRUE); break;
case DEV_CLOSE: r = do_close(cdp, m_ptr); break;
case DEV_READ_S: r = do_transfer(cdp, m_ptr, FALSE); break;
case DEV_WRITE_S: r = do_transfer(cdp, m_ptr, TRUE); break;
case DEV_IOCTL_S: r = do_ioctl(cdp, m_ptr); break;
case CANCEL: r = do_cancel(cdp, m_ptr); break;
case DEV_SELECT: r = do_select(cdp, m_ptr); break;
default:
if (cdp->cdr_other)
cdp->cdr_other(m_ptr, ipc_status);
return; /* do not send a reply */
}
chardriver_reply(m_ptr, ipc_status, r);
}
/*===========================================================================*
* chardriver_terminate *
*===========================================================================*/
void chardriver_terminate(void)
{
/* Break out of the main loop after finishing the current request. */
running = FALSE;
sef_cancel();
}
/*===========================================================================*
@ -378,59 +534,13 @@ void chardriver_task(struct chardriver *cdp)
* it out, and sends a reply.
*/
while (running) {
if ((r = sef_receive_status(ANY, &mess, &ipc_status)) != OK)
if ((r = sef_receive_status(ANY, &mess, &ipc_status)) != OK) {
if (r == EINTR && !running)
break;
panic("driver_receive failed: %d", r);
}
chardriver_process(cdp, &mess, ipc_status);
}
}
/*===========================================================================*
* do_nop *
*===========================================================================*/
int do_nop(message *UNUSED(mp))
{
return(OK);
}
/*===========================================================================*
* nop_ioctl *
*===========================================================================*/
int nop_ioctl(message *UNUSED(mp))
{
return(ENOTTY);
}
/*===========================================================================*
* nop_alarm *
*===========================================================================*/
void nop_alarm(message *UNUSED(mp))
{
/* Ignore the leftover alarm. */
}
/*===========================================================================*
* nop_cleanup *
*===========================================================================*/
void nop_cleanup(void)
{
/* Nothing to clean up. */
}
/*===========================================================================*
* nop_cancel *
*===========================================================================*/
int nop_cancel(message *UNUSED(m))
{
/* Nothing to do for cancel. */
return(OK);
}
/*===========================================================================*
* nop_select *
*===========================================================================*/
int nop_select(message *UNUSED(m))
{
/* Nothing to do for select. */
return(OK);
}