Split block/character protocols and libdriver
This patch separates the character and block driver communication protocols. The old character protocol remains the same, but a new block protocol is introduced. The libdriver library is replaced by two new libraries: libchardriver and libblockdriver. Their exposed API, and drivers that use them, have been updated accordingly. Together, libbdev and libblockdriver now completely abstract away the message format used by the block protocol. As the memory driver is both a character and a block device driver, it now implements its own message loop. The most important semantic change made to the block protocol is that it is no longer possible to return both partial results and an error for a single transfer. This simplifies the interaction between the caller and the driver, as the I/O vector no longer needs to be copied back. Also, drivers are now no longer supposed to decide based on the layout of the I/O vector when a transfer should be cut short. Put simply, transfers are now supposed to either succeed completely, or result in an error. After this patch, the state of the various pieces is as follows: - block protocol: stable - libbdev API: stable for synchronous communication - libblockdriver API: needs slight revision (the drvlib/partition API in particular; the threading API will also change shortly) - character protocol: needs cleanup - libchardriver API: needs cleanup accordingly - driver restarts: largely unsupported until endpoint changes are reintroduced As a side effect, this patch eliminates several bugs, hacks, and gcc -Wall and -W warnings all over the place. It probably introduces a few new ones, too. Update warning: this patch changes the protocol between MFS and disk drivers, so in order to use old/new images, the MFS from the ramdisk must be used to mount all file systems.
This commit is contained in:
@@ -16,9 +16,9 @@ LIBCOMPAT_DIR?=
|
||||
LIBMINLIB_DIR?=
|
||||
LIBASYN_DIR?=
|
||||
|
||||
SUBDIR= csu ${LIBCOMPAT_DIR} ${LIBC_DIR} libdriver libnetdriver \
|
||||
libedit ${LIBM_DIR} libsys libtimers ${LIBUTIL_DIR} libl libhgfs \
|
||||
libz libfetch libvtreefs libaudiodriver libmthread \
|
||||
SUBDIR= csu ${LIBCOMPAT_DIR} ${LIBC_DIR} libblockdriver libchardriver \
|
||||
libnetdriver libedit ${LIBM_DIR} libsys libtimers ${LIBUTIL_DIR} \
|
||||
libl libhgfs libz libfetch libvtreefs libaudiodriver libmthread \
|
||||
libexec libdevman libusb ${LIBMINLIB_DIR} ${LIBASYN_DIR} \
|
||||
libddekit libminixfs libbdev
|
||||
|
||||
|
||||
@@ -83,7 +83,7 @@ EXTERN _PROTOTYPE( int sef_cb_lu_state_isvalid, (int state) );
|
||||
EXTERN _PROTOTYPE( void sef_cb_lu_state_dump, (int state) );
|
||||
PUBLIC int is_status_msg_expected = FALSE;
|
||||
|
||||
PUBLIC int main(int argc, char *argv[])
|
||||
PUBLIC int main(void)
|
||||
{
|
||||
int r, caller;
|
||||
message mess, repl_mess;
|
||||
@@ -255,7 +255,7 @@ PRIVATE int init_driver(void) {
|
||||
irq_hook_set = TRUE; /* now signal handler knows it must unregister policy*/
|
||||
|
||||
/* Announce we are up! */
|
||||
driver_announce();
|
||||
chardriver_announce();
|
||||
|
||||
return OK;
|
||||
}
|
||||
|
||||
@@ -32,9 +32,10 @@ static int bdev_opcl(int req, dev_t dev, int access)
|
||||
*/
|
||||
message m;
|
||||
|
||||
memset(&m, 0, sizeof(m));
|
||||
m.m_type = req;
|
||||
m.DEVICE = minor(dev);
|
||||
m.COUNT = access;
|
||||
m.BDEV_MINOR = minor(dev);
|
||||
m.BDEV_ACCESS = access;
|
||||
|
||||
return bdev_sendrec(dev, &m);
|
||||
}
|
||||
@@ -45,7 +46,7 @@ int bdev_open(dev_t dev, int access)
|
||||
* File system usage note: typically called from mount, after bdev_driver.
|
||||
*/
|
||||
|
||||
return bdev_opcl(DEV_OPEN, dev, access);
|
||||
return bdev_opcl(BDEV_OPEN, dev, access);
|
||||
}
|
||||
|
||||
int bdev_close(dev_t dev)
|
||||
@@ -54,11 +55,11 @@ int bdev_close(dev_t dev)
|
||||
* File system usage note: typically called from unmount.
|
||||
*/
|
||||
|
||||
return bdev_opcl(DEV_CLOSE, dev, 0);
|
||||
return bdev_opcl(BDEV_CLOSE, dev, 0);
|
||||
}
|
||||
|
||||
static int bdev_rdwt_setup(int req, dev_t dev, u64_t pos, char *buf, int count,
|
||||
int UNUSED(flags), message *m)
|
||||
static int bdev_rdwt_setup(int req, dev_t dev, u64_t pos, char *buf,
|
||||
size_t count, int flags, message *m)
|
||||
{
|
||||
/* Set up a single-buffer read/write request.
|
||||
*/
|
||||
@@ -66,10 +67,12 @@ static int bdev_rdwt_setup(int req, dev_t dev, u64_t pos, char *buf, int count,
|
||||
cp_grant_id_t grant;
|
||||
int access;
|
||||
|
||||
assert((ssize_t) count >= 0);
|
||||
|
||||
if ((endpt = bdev_driver_get(dev)) == NONE)
|
||||
return EDEADSRCDST;
|
||||
|
||||
access = (req == DEV_READ_S) ? CPF_WRITE : CPF_READ;
|
||||
access = (req == BDEV_READ) ? CPF_WRITE : CPF_READ;
|
||||
|
||||
grant = cpf_grant_direct(endpt, (vir_bytes) buf, count, access);
|
||||
|
||||
@@ -78,12 +81,14 @@ static int bdev_rdwt_setup(int req, dev_t dev, u64_t pos, char *buf, int count,
|
||||
return EINVAL;
|
||||
}
|
||||
|
||||
memset(m, 0, sizeof(*m));
|
||||
m->m_type = req;
|
||||
m->DEVICE = minor(dev);
|
||||
m->POSITION = ex64lo(pos);
|
||||
m->HIGHPOS = ex64hi(pos);
|
||||
m->COUNT = count;
|
||||
m->IO_GRANT = (void *) grant;
|
||||
m->BDEV_MINOR = minor(dev);
|
||||
m->BDEV_POS_LO = ex64lo(pos);
|
||||
m->BDEV_POS_HI = ex64hi(pos);
|
||||
m->BDEV_COUNT = count;
|
||||
m->BDEV_GRANT = grant;
|
||||
m->BDEV_FLAGS = flags;
|
||||
|
||||
return OK;
|
||||
}
|
||||
@@ -92,15 +97,12 @@ static void bdev_rdwt_cleanup(message *m)
|
||||
{
|
||||
/* Clean up a single-buffer read/write request.
|
||||
*/
|
||||
cp_grant_id_t grant;
|
||||
|
||||
grant = (cp_grant_id_t) m->IO_GRANT;
|
||||
|
||||
cpf_revoke(grant);
|
||||
cpf_revoke(m->BDEV_GRANT);
|
||||
}
|
||||
|
||||
static int bdev_rdwt(int req, dev_t dev, u64_t pos, char *buf, int count,
|
||||
int flags)
|
||||
static ssize_t bdev_rdwt(int req, dev_t dev, u64_t pos, char *buf,
|
||||
size_t count, int flags)
|
||||
{
|
||||
/* Perform a read or write call using a single buffer.
|
||||
*/
|
||||
@@ -118,11 +120,11 @@ static int bdev_rdwt(int req, dev_t dev, u64_t pos, char *buf, int count,
|
||||
}
|
||||
|
||||
static int bdev_vrdwt_setup(int req, dev_t dev, u64_t pos, iovec_t *vec,
|
||||
int count, int UNUSED(flags), message *m, iovec_s_t *gvec,
|
||||
cp_grant_id_t *grants, vir_bytes *size)
|
||||
int count, int flags, message *m, iovec_s_t *gvec)
|
||||
{
|
||||
/* Set up a vectored read/write request.
|
||||
*/
|
||||
ssize_t size;
|
||||
endpoint_t endpt;
|
||||
cp_grant_id_t grant;
|
||||
int i, access;
|
||||
@@ -132,154 +134,118 @@ static int bdev_vrdwt_setup(int req, dev_t dev, u64_t pos, iovec_t *vec,
|
||||
if ((endpt = bdev_driver_get(dev)) == NONE)
|
||||
return EDEADSRCDST;
|
||||
|
||||
access = (req == DEV_GATHER_S) ? CPF_WRITE : CPF_READ;
|
||||
*size = 0;
|
||||
access = (req == BDEV_GATHER) ? CPF_WRITE : CPF_READ;
|
||||
size = 0;
|
||||
|
||||
for (i = 0; i < count; i++) {
|
||||
grants[i] = cpf_grant_direct(endpt, vec[i].iov_addr, vec[i].iov_size,
|
||||
grant = cpf_grant_direct(endpt, vec[i].iov_addr, vec[i].iov_size,
|
||||
access);
|
||||
|
||||
if (!GRANT_VALID(grants[i])) {
|
||||
if (!GRANT_VALID(grant)) {
|
||||
printf("bdev: unable to allocate grant!\n");
|
||||
|
||||
for (i--; i >= 0; i--)
|
||||
cpf_revoke(grants[i]);
|
||||
cpf_revoke(gvec[i].iov_grant);
|
||||
|
||||
return EINVAL;
|
||||
}
|
||||
|
||||
/* We keep a separate grants array to prevent local leaks if the driver
|
||||
* ends up clobbering the grant vector. Future protocol updates should
|
||||
* make the grant for the vector read-only.
|
||||
*/
|
||||
gvec[i].iov_grant = grants[i];
|
||||
gvec[i].iov_grant = grant;
|
||||
gvec[i].iov_size = vec[i].iov_size;
|
||||
|
||||
assert(*size + vec[i].iov_size > *size);
|
||||
assert((ssize_t) (size + vec[i].iov_size) > size);
|
||||
|
||||
*size += vec[i].iov_size;
|
||||
size += vec[i].iov_size;
|
||||
}
|
||||
|
||||
grant = cpf_grant_direct(endpt, (vir_bytes) gvec, sizeof(gvec[0]) * count,
|
||||
CPF_READ | CPF_WRITE);
|
||||
CPF_READ);
|
||||
|
||||
if (!GRANT_VALID(grant)) {
|
||||
printf("bdev: unable to allocate grant!\n");
|
||||
|
||||
for (i = count - 1; i >= 0; i--)
|
||||
cpf_revoke(grants[i]);
|
||||
cpf_revoke(gvec[i].iov_grant);
|
||||
|
||||
return EINVAL;
|
||||
}
|
||||
|
||||
memset(m, 0, sizeof(*m));
|
||||
m->m_type = req;
|
||||
m->DEVICE = minor(dev);
|
||||
m->POSITION = ex64lo(pos);
|
||||
m->HIGHPOS = ex64hi(pos);
|
||||
m->COUNT = count;
|
||||
m->IO_GRANT = (void *) grant;
|
||||
m->BDEV_MINOR = minor(dev);
|
||||
m->BDEV_POS_LO = ex64lo(pos);
|
||||
m->BDEV_POS_HI = ex64hi(pos);
|
||||
m->BDEV_COUNT = count;
|
||||
m->BDEV_GRANT = grant;
|
||||
m->BDEV_FLAGS = flags;
|
||||
|
||||
return OK;
|
||||
}
|
||||
|
||||
static void bdev_vrdwt_cleanup(message *m, cp_grant_id_t *grants)
|
||||
static void bdev_vrdwt_cleanup(message *m, iovec_s_t *gvec)
|
||||
{
|
||||
/* Clean up a vectored read/write request.
|
||||
*/
|
||||
cp_grant_id_t grant;
|
||||
int i;
|
||||
|
||||
grant = (cp_grant_id_t) m->IO_GRANT;
|
||||
grant = m->BDEV_GRANT;
|
||||
|
||||
cpf_revoke(grant);
|
||||
|
||||
for (i = m->COUNT - 1; i >= 0; i--)
|
||||
cpf_revoke(grants[i]);
|
||||
for (i = m->BDEV_COUNT - 1; i >= 0; i--)
|
||||
cpf_revoke(gvec[i].iov_grant);
|
||||
}
|
||||
|
||||
static int bdev_vrdwt_adjust(dev_t dev, iovec_s_t *gvec, int count,
|
||||
vir_bytes *size)
|
||||
static ssize_t bdev_vrdwt(int req, dev_t dev, u64_t pos, iovec_t *vec,
|
||||
int count, int flags)
|
||||
{
|
||||
/* Adjust the number of bytes transferred, by subtracting from it the number of
|
||||
* bytes *not* transferred according to the result vector.
|
||||
*/
|
||||
int i;
|
||||
|
||||
for (i = 0; i < count; i++) {
|
||||
if (*size < gvec[i].iov_size) {
|
||||
printf("bdev: driver (%d) returned bad vector\n",
|
||||
bdev_driver_get(dev));
|
||||
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
*size -= gvec[i].iov_size;
|
||||
}
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static int bdev_vrdwt(int req, dev_t dev, u64_t pos, iovec_t *vec, int count,
|
||||
int flags, vir_bytes *size)
|
||||
{
|
||||
/* Perform a read or write call using a vector of buffer.
|
||||
/* Perform a read or write call using a vector of buffers.
|
||||
*/
|
||||
iovec_s_t gvec[NR_IOREQS];
|
||||
cp_grant_id_t grants[NR_IOREQS];
|
||||
message m;
|
||||
int r;
|
||||
|
||||
if ((r = bdev_vrdwt_setup(req, dev, pos, vec, count, flags, &m, gvec,
|
||||
grants, size)) != OK) {
|
||||
*size = 0;
|
||||
if ((r = bdev_vrdwt_setup(req, dev, pos, vec, count, flags, &m, gvec)) != OK)
|
||||
return r;
|
||||
}
|
||||
|
||||
r = bdev_sendrec(dev, &m);
|
||||
|
||||
bdev_vrdwt_cleanup(&m, grants);
|
||||
|
||||
/* Also return the number of bytes transferred. */
|
||||
if (!bdev_vrdwt_adjust(dev, gvec, count, size)) {
|
||||
*size = 0;
|
||||
r = EIO;
|
||||
}
|
||||
bdev_vrdwt_cleanup(&m, gvec);
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
int bdev_read(dev_t dev, u64_t pos, char *buf, int count, int flags)
|
||||
ssize_t bdev_read(dev_t dev, u64_t pos, char *buf, size_t count, int flags)
|
||||
{
|
||||
/* Perform a read call into a single buffer.
|
||||
*/
|
||||
|
||||
return bdev_rdwt(DEV_READ_S, dev, pos, buf, count, flags);
|
||||
return bdev_rdwt(BDEV_READ, dev, pos, buf, count, flags);
|
||||
}
|
||||
|
||||
int bdev_write(dev_t dev, u64_t pos, char *buf, int count, int flags)
|
||||
ssize_t bdev_write(dev_t dev, u64_t pos, char *buf, size_t count, int flags)
|
||||
{
|
||||
/* Perform a write call from a single buffer.
|
||||
*/
|
||||
|
||||
return bdev_rdwt(DEV_WRITE_S, dev, pos, buf, count, flags);
|
||||
return bdev_rdwt(BDEV_WRITE, dev, pos, buf, count, flags);
|
||||
}
|
||||
|
||||
int bdev_gather(dev_t dev, u64_t pos, iovec_t *vec, int count, int flags,
|
||||
vir_bytes *size)
|
||||
ssize_t bdev_gather(dev_t dev, u64_t pos, iovec_t *vec, int count, int flags)
|
||||
{
|
||||
/* Perform a read call into a vector of buffers.
|
||||
*/
|
||||
|
||||
return bdev_vrdwt(DEV_GATHER_S, dev, pos, vec, count, flags, size);
|
||||
return bdev_vrdwt(BDEV_GATHER, dev, pos, vec, count, flags);
|
||||
}
|
||||
|
||||
int bdev_scatter(dev_t dev, u64_t pos, iovec_t *vec, int count, int flags,
|
||||
vir_bytes *size)
|
||||
ssize_t bdev_scatter(dev_t dev, u64_t pos, iovec_t *vec, int count, int flags)
|
||||
{
|
||||
/* Perform a write call from a vector of buffers.
|
||||
*/
|
||||
|
||||
return bdev_vrdwt(DEV_SCATTER_S, dev, pos, vec, count, flags, size);
|
||||
return bdev_vrdwt(BDEV_SCATTER, dev, pos, vec, count, flags);
|
||||
}
|
||||
|
||||
static int bdev_ioctl_setup(dev_t dev, int request, void *buf, message *m)
|
||||
@@ -311,10 +277,11 @@ static int bdev_ioctl_setup(dev_t dev, int request, void *buf, message *m)
|
||||
return EINVAL;
|
||||
}
|
||||
|
||||
m->m_type = DEV_IOCTL_S;
|
||||
m->DEVICE = minor(dev);
|
||||
m->REQUEST = request;
|
||||
m->IO_GRANT = (void *) grant;
|
||||
memset(m, 0, sizeof(*m));
|
||||
m->m_type = BDEV_IOCTL;
|
||||
m->BDEV_MINOR = minor(dev);
|
||||
m->BDEV_REQUEST = request;
|
||||
m->BDEV_GRANT = grant;
|
||||
|
||||
return OK;
|
||||
}
|
||||
@@ -323,11 +290,8 @@ static void bdev_ioctl_cleanup(message *m)
|
||||
{
|
||||
/* Clean up an I/O control request.
|
||||
*/
|
||||
cp_grant_id_t grant;
|
||||
|
||||
grant = (cp_grant_id_t) m->IO_GRANT;
|
||||
|
||||
cpf_revoke(grant);
|
||||
cpf_revoke(m->BDEV_GRANT);
|
||||
}
|
||||
|
||||
int bdev_ioctl(dev_t dev, int request, void *buf)
|
||||
|
||||
@@ -12,7 +12,8 @@ static void bdev_cancel(dev_t dev)
|
||||
* permanently unusable, and clean up any associated calls and resources.
|
||||
*/
|
||||
|
||||
printf("bdev: driver for major %d crashed\n", major(dev));
|
||||
printf("bdev: driver for major %d (endpoint %d) crashed\n",
|
||||
major(dev), bdev_driver_get(dev));
|
||||
|
||||
/* Mark the driver as unusable. */
|
||||
bdev_driver_clear(dev);
|
||||
@@ -27,18 +28,13 @@ void bdev_update(dev_t dev, endpoint_t endpt)
|
||||
old_endpt = bdev_driver_get(dev);
|
||||
|
||||
bdev_driver_set(dev, endpt);
|
||||
|
||||
/* If updating the driver causes an endpoint change, the driver has
|
||||
* restarted.
|
||||
*/
|
||||
if (old_endpt != NONE && old_endpt != endpt)
|
||||
bdev_cancel(dev);
|
||||
}
|
||||
|
||||
int bdev_sendrec(dev_t dev, const message *m_orig)
|
||||
{
|
||||
/* Send a request to the given device, and wait for the reply.
|
||||
*/
|
||||
static long id = 0;
|
||||
endpoint_t endpt;
|
||||
message m;
|
||||
int r;
|
||||
@@ -49,7 +45,7 @@ int bdev_sendrec(dev_t dev, const message *m_orig)
|
||||
|
||||
/* Send the request and block until we receive a reply. */
|
||||
m = *m_orig;
|
||||
m.USER_ENDPT = (endpoint_t) -1; /* synchronous request; no ID */
|
||||
m.BDEV_ID = ++id;
|
||||
|
||||
r = sendrec(endpt, &m);
|
||||
|
||||
@@ -65,25 +61,25 @@ int bdev_sendrec(dev_t dev, const message *m_orig)
|
||||
return r;
|
||||
}
|
||||
|
||||
if (m.m_type != TASK_REPLY) {
|
||||
if (m.m_type != BDEV_REPLY) {
|
||||
printf("bdev: driver (%d) sent weird response (%d)\n",
|
||||
endpt, m.m_type);
|
||||
return EIO;
|
||||
}
|
||||
|
||||
/* ERESTART signifies a driver restart. Again, we do not support this yet. */
|
||||
if (m.REP_STATUS == ERESTART) {
|
||||
if (m.BDEV_STATUS == ERESTART) {
|
||||
bdev_cancel(dev);
|
||||
|
||||
return EDEADSRCDST;
|
||||
}
|
||||
|
||||
if (m.REP_ENDPT != (endpoint_t) -1) {
|
||||
printf("bdev: driver (%d) sent invalid response (%d)\n",
|
||||
endpt, m.REP_ENDPT);
|
||||
if (m.BDEV_ID != id) {
|
||||
printf("bdev: driver (%d) sent invalid response (%ld)\n",
|
||||
endpt, m.BDEV_ID);
|
||||
return EIO;
|
||||
}
|
||||
|
||||
/* We got a reply to our request. */
|
||||
return m.REP_STATUS;
|
||||
/* Return the result of our request. */
|
||||
return m.BDEV_STATUS;
|
||||
}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
# Makefile for libdriver
|
||||
# Makefile for libblockdriver
|
||||
.include <bsd.own.mk>
|
||||
|
||||
LIB= driver
|
||||
LIB= blockdriver
|
||||
|
||||
SRCS= driver.c drvlib.c driver_st.c driver_mt.c mq.c event.c
|
||||
|
||||
382
lib/libblockdriver/driver.c
Normal file
382
lib/libblockdriver/driver.c
Normal file
@@ -0,0 +1,382 @@
|
||||
/* This file contains the device independent block driver interface.
|
||||
*
|
||||
* Block drivers support the following requests. Message format m10 is used.
|
||||
* Field names are prefixed with BDEV_. Separate field names are used for the
|
||||
* "access" and "request" fields.
|
||||
*
|
||||
* m_type MINOR COUNT GRANT FLAGS ID POS_LO POS_HI
|
||||
* +--------------+--------+----------+-------+-------+------+------+------+
|
||||
* | BDEV_OPEN | minor | access | | | id | | |
|
||||
* |--------------+--------+----------+-------+-------+------+------+------|
|
||||
* | BDEV_CLOSE | minor | | | | id | | |
|
||||
* |--------------+--------+----------+-------+-------+------+------+------|
|
||||
* | BDEV_READ | minor | bytes | grant | flags | id | position |
|
||||
* |--------------+--------+----------+-------+-------+------+------+------|
|
||||
* | BDEV_WRITE | minor | bytes | grant | flags | id | position |
|
||||
* |--------------+--------+----------+-------+-------+------+------+------|
|
||||
* | BDEV_GATHER | minor | elements | grant | flags | id | position |
|
||||
* |--------------+--------+----------+-------+-------+------+------+------|
|
||||
* | BDEV_SCATTER | minor | elements | grant | flags | id | position |
|
||||
* |--------------+--------+----------+-------+-------+------+------+------|
|
||||
* | BDEV_IOCTL | minor | request | grant | flags | id | | |
|
||||
* -------------------------------------------------------------------------
|
||||
*
|
||||
* The following reply message is used for all requests.
|
||||
*
|
||||
* m_type STATUS ID
|
||||
* +--------------+--------+----------+-------+-------+------+------+------+
|
||||
* | BDEV_REPLY | status | | | | id | | |
|
||||
* -------------------------------------------------------------------------
|
||||
*
|
||||
* Changes:
|
||||
* 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)
|
||||
* Sep 15, 2004 added SYN_ALARM type for timeouts (Jorrit N. Herder)
|
||||
* Jul 23, 2004 removed kernel dependencies (Jorrit N. Herder)
|
||||
* Apr 02, 1992 constructed from AT wini and floppy driver (Kees J. Bot)
|
||||
*/
|
||||
|
||||
#include <minix/drivers.h>
|
||||
#include <minix/blockdriver.h>
|
||||
#include <minix/ds.h>
|
||||
#include <sys/ioc_disk.h>
|
||||
|
||||
#include "driver.h"
|
||||
#include "mq.h"
|
||||
|
||||
/* Management data for opened devices. */
|
||||
PRIVATE int open_devs[MAX_NR_OPEN_DEVICES];
|
||||
PRIVATE int next_open_devs_slot = 0;
|
||||
|
||||
/*===========================================================================*
|
||||
* clear_open_devs *
|
||||
*===========================================================================*/
|
||||
PRIVATE void clear_open_devs(void)
|
||||
{
|
||||
/* Reset the set of previously opened minor devices. */
|
||||
next_open_devs_slot = 0;
|
||||
}
|
||||
|
||||
/*===========================================================================*
|
||||
* is_open_dev *
|
||||
*===========================================================================*/
|
||||
PRIVATE int is_open_dev(int device)
|
||||
{
|
||||
/* 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)
|
||||
return TRUE;
|
||||
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
/*===========================================================================*
|
||||
* set_open_dev *
|
||||
*===========================================================================*/
|
||||
PRIVATE void set_open_dev(int device)
|
||||
{
|
||||
/* 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;
|
||||
next_open_devs_slot++;
|
||||
}
|
||||
|
||||
/*===========================================================================*
|
||||
* blockdriver_announce *
|
||||
*===========================================================================*/
|
||||
PUBLIC void blockdriver_announce(void)
|
||||
{
|
||||
/* Announce we are up after a fresh start or a restart. */
|
||||
int r;
|
||||
char key[DS_MAX_KEYLEN];
|
||||
char label[DS_MAX_KEYLEN];
|
||||
char *driver_prefix = "drv.blk.";
|
||||
|
||||
/* Callers are allowed to use sendrec to communicate with drivers.
|
||||
* For this reason, there may blocked callers when a driver restarts.
|
||||
* Ask the kernel to unblock them (if any).
|
||||
*/
|
||||
#if USE_STATECTL
|
||||
if ((r = sys_statectl(SYS_STATE_CLEAR_IPC_REFS)) != OK)
|
||||
panic("blockdriver_init: sys_statectl failed: %d", r);
|
||||
#endif
|
||||
|
||||
/* Publish a driver up event. */
|
||||
if ((r = ds_retrieve_label_name(label, getprocnr())) != OK)
|
||||
panic("blockdriver_init: unable to get own label: %d", r);
|
||||
|
||||
snprintf(key, DS_MAX_KEYLEN, "%s%s", driver_prefix, label);
|
||||
if ((r = ds_publish_u32(key, DS_DRIVER_UP, DSF_OVERWRITE)) != OK)
|
||||
panic("blockdriver_init: unable to publish driver up event: %d", r);
|
||||
|
||||
/* Expect an open for any device before serving regular driver requests. */
|
||||
clear_open_devs();
|
||||
|
||||
/* Initialize or reset the message queue. */
|
||||
mq_init();
|
||||
}
|
||||
|
||||
/*===========================================================================*
|
||||
* blockdriver_reply *
|
||||
*===========================================================================*/
|
||||
PUBLIC void blockdriver_reply(message *m_ptr, int ipc_status, int reply)
|
||||
{
|
||||
/* Reply to a block request sent to the driver. */
|
||||
endpoint_t caller_e;
|
||||
long id;
|
||||
int r;
|
||||
|
||||
if (reply == EDONTREPLY)
|
||||
return;
|
||||
|
||||
caller_e = m_ptr->m_source;
|
||||
id = m_ptr->BDEV_ID;
|
||||
|
||||
memset(m_ptr, 0, sizeof(*m_ptr));
|
||||
|
||||
m_ptr->m_type = BDEV_REPLY;
|
||||
m_ptr->BDEV_STATUS = reply;
|
||||
m_ptr->BDEV_ID = id;
|
||||
|
||||
/* If we would block sending the message, send it asynchronously. */
|
||||
if (IPC_STATUS_CALL(ipc_status) == SENDREC)
|
||||
r = sendnb(caller_e, m_ptr);
|
||||
else
|
||||
r = asynsend(caller_e, m_ptr);
|
||||
|
||||
if (r != OK)
|
||||
printf("blockdriver_reply: unable to send reply to %d: %d\n",
|
||||
caller_e, r);
|
||||
}
|
||||
|
||||
/*===========================================================================*
|
||||
* do_open *
|
||||
*===========================================================================*/
|
||||
PRIVATE int do_open(struct blockdriver *bdp, message *mp)
|
||||
{
|
||||
/* Open a minor device. */
|
||||
|
||||
return (*bdp->bdr_open)(mp->BDEV_MINOR, mp->BDEV_ACCESS);
|
||||
}
|
||||
|
||||
/*===========================================================================*
|
||||
* do_close *
|
||||
*===========================================================================*/
|
||||
PRIVATE int do_close(struct blockdriver *bdp, message *mp)
|
||||
{
|
||||
/* Close a minor device. */
|
||||
|
||||
return (*bdp->bdr_close)(mp->BDEV_MINOR);
|
||||
}
|
||||
|
||||
/*===========================================================================*
|
||||
* do_rdwt *
|
||||
*===========================================================================*/
|
||||
PRIVATE int do_rdwt(struct blockdriver *bdp, message *mp)
|
||||
{
|
||||
/* Carry out a single read or write request. */
|
||||
iovec_t iovec1;
|
||||
u64_t position;
|
||||
int do_write;
|
||||
ssize_t r;
|
||||
|
||||
/* Disk address? Address and length of the user buffer? */
|
||||
if (mp->BDEV_COUNT < 0) return EINVAL;
|
||||
|
||||
/* Create a one element scatter/gather vector for the buffer. */
|
||||
iovec1.iov_addr = mp->BDEV_GRANT;
|
||||
iovec1.iov_size = mp->BDEV_COUNT;
|
||||
|
||||
/* Transfer bytes from/to the device. */
|
||||
do_write = (mp->m_type == BDEV_WRITE);
|
||||
position = make64(mp->BDEV_POS_LO, mp->BDEV_POS_HI);
|
||||
|
||||
r = (*bdp->bdr_transfer)(mp->BDEV_MINOR, do_write, position, mp->m_source,
|
||||
&iovec1, 1, mp->BDEV_FLAGS);
|
||||
|
||||
/* Return the number of bytes transferred or an error code. */
|
||||
return r;
|
||||
}
|
||||
|
||||
/*===========================================================================*
|
||||
* do_vrdwt *
|
||||
*===========================================================================*/
|
||||
PRIVATE int do_vrdwt(struct blockdriver *bdp, message *mp)
|
||||
{
|
||||
/* Carry out an device read or write to/from a vector of buffers. */
|
||||
iovec_t iovec[NR_IOREQS];
|
||||
unsigned nr_req;
|
||||
u64_t position;
|
||||
int i, do_write;
|
||||
ssize_t r, size;
|
||||
|
||||
/* Copy the vector from the caller to kernel space. */
|
||||
nr_req = mp->BDEV_COUNT; /* Length of I/O vector */
|
||||
if (nr_req > NR_IOREQS) nr_req = NR_IOREQS;
|
||||
|
||||
if (OK != sys_safecopyfrom(mp->m_source, (vir_bytes) mp->BDEV_GRANT,
|
||||
0, (vir_bytes) iovec, nr_req * sizeof(iovec[0]), D)) {
|
||||
printf("blockdriver: bad I/O vector by: %d\n", mp->m_source);
|
||||
return EINVAL;
|
||||
}
|
||||
|
||||
/* Check for overflow condition. */
|
||||
for (i = size = 0; i < nr_req; i++) {
|
||||
if ((ssize_t) (size + iovec[i].iov_size) < size) return EINVAL;
|
||||
size += iovec[i].iov_size;
|
||||
}
|
||||
|
||||
/* Transfer bytes from/to the device. */
|
||||
do_write = (mp->m_type == BDEV_SCATTER);
|
||||
position = make64(mp->BDEV_POS_LO, mp->BDEV_POS_HI);
|
||||
|
||||
r = (*bdp->bdr_transfer)(mp->BDEV_MINOR, do_write, position, mp->m_source,
|
||||
iovec, nr_req, mp->BDEV_FLAGS);
|
||||
|
||||
/* Return the number of bytes transferred or an error code. */
|
||||
return r;
|
||||
}
|
||||
|
||||
/*===========================================================================*
|
||||
* do_ioctl *
|
||||
*===========================================================================*/
|
||||
PRIVATE int do_ioctl(struct blockdriver *bdp, message *mp)
|
||||
{
|
||||
/* Carry out an I/O control request. For now, we handle setting/getting
|
||||
* partitions here, and let the driver handle any other requests.
|
||||
*/
|
||||
struct device *dv;
|
||||
struct partition entry;
|
||||
unsigned int request;
|
||||
cp_grant_id_t grant;
|
||||
dev_t minor;
|
||||
int r;
|
||||
|
||||
minor = mp->BDEV_MINOR;
|
||||
request = mp->BDEV_REQUEST;
|
||||
grant = mp->BDEV_GRANT;
|
||||
|
||||
switch (request) {
|
||||
case DIOCSETP:
|
||||
/* Copy just this one partition table entry. */
|
||||
r = sys_safecopyfrom(mp->m_source, grant, 0, (vir_bytes) &entry,
|
||||
sizeof(entry), D);
|
||||
if (r != OK)
|
||||
return r;
|
||||
|
||||
if ((dv = (*bdp->bdr_part)(minor)) == NULL)
|
||||
return ENXIO;
|
||||
dv->dv_base = entry.base;
|
||||
dv->dv_size = entry.size;
|
||||
|
||||
break;
|
||||
|
||||
case DIOCGETP:
|
||||
/* Return a partition table entry and the geometry of the drive. */
|
||||
if ((dv = (*bdp->bdr_part)(minor)) == NULL)
|
||||
return ENXIO;
|
||||
entry.base = dv->dv_base;
|
||||
entry.size = dv->dv_size;
|
||||
if (bdp->bdr_geometry) {
|
||||
(*bdp->bdr_geometry)(minor, &entry);
|
||||
} else {
|
||||
/* The driver doesn't care -- make up fake geometry. */
|
||||
entry.cylinders = div64u(entry.size, SECTOR_SIZE);
|
||||
entry.heads = 64;
|
||||
entry.sectors = 32;
|
||||
}
|
||||
|
||||
r = sys_safecopyto(mp->m_source, grant, 0, (vir_bytes) &entry,
|
||||
sizeof(entry), D);
|
||||
|
||||
break;
|
||||
|
||||
default:
|
||||
if (bdp->bdr_ioctl)
|
||||
r = (*bdp->bdr_ioctl)(minor, request, mp->m_source, grant);
|
||||
else
|
||||
r = EINVAL;
|
||||
}
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
/*===========================================================================*
|
||||
* blockdriver_handle_notify *
|
||||
*===========================================================================*/
|
||||
PUBLIC void blockdriver_handle_notify(struct blockdriver *bdp, 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 HARDWARE:
|
||||
if (bdp->bdr_intr)
|
||||
(*bdp->bdr_intr)(m_ptr->NOTIFY_ARG);
|
||||
break;
|
||||
|
||||
case CLOCK:
|
||||
if (bdp->bdr_alarm)
|
||||
(*bdp->bdr_alarm)(m_ptr->NOTIFY_TIMESTAMP);
|
||||
break;
|
||||
|
||||
default:
|
||||
if (bdp->bdr_other)
|
||||
(void) (*bdp->bdr_other)(m_ptr);
|
||||
}
|
||||
}
|
||||
|
||||
/*===========================================================================*
|
||||
* blockdriver_handle_request *
|
||||
*===========================================================================*/
|
||||
PUBLIC int blockdriver_handle_request(struct blockdriver *bdp, 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_BDEV_RQ(m_ptr->m_type) && !is_open_dev(m_ptr->BDEV_MINOR)) {
|
||||
/* Reply ERESTART to spurious requests for unopened devices. */
|
||||
if (m_ptr->m_type != BDEV_OPEN)
|
||||
return ERESTART;
|
||||
|
||||
/* Mark the device as opened otherwise. */
|
||||
set_open_dev(m_ptr->BDEV_MINOR);
|
||||
}
|
||||
|
||||
/* Call the appropriate function(s) for this request. */
|
||||
switch (m_ptr->m_type) {
|
||||
case BDEV_OPEN: r = do_open(bdp, m_ptr); break;
|
||||
case BDEV_CLOSE: r = do_close(bdp, m_ptr); break;
|
||||
case BDEV_READ:
|
||||
case BDEV_WRITE: r = do_rdwt(bdp, m_ptr); break;
|
||||
case BDEV_GATHER:
|
||||
case BDEV_SCATTER: r = do_vrdwt(bdp, m_ptr); break;
|
||||
case BDEV_IOCTL: r = do_ioctl(bdp, m_ptr); break;
|
||||
default:
|
||||
if (bdp->bdr_other)
|
||||
r = bdp->bdr_other(m_ptr);
|
||||
else
|
||||
r = EINVAL;
|
||||
}
|
||||
|
||||
/* Let the driver perform any cleanup. */
|
||||
if (bdp->bdr_cleanup != NULL)
|
||||
(*bdp->bdr_cleanup)();
|
||||
|
||||
return r;
|
||||
}
|
||||
11
lib/libblockdriver/driver.h
Normal file
11
lib/libblockdriver/driver.h
Normal file
@@ -0,0 +1,11 @@
|
||||
#ifndef _BLOCKDRIVER_DRIVER_H
|
||||
#define _BLOCKDRIVER_DRIVER_H
|
||||
|
||||
_PROTOTYPE( void blockdriver_handle_notify, (struct blockdriver *bdp,
|
||||
message *m_ptr) );
|
||||
_PROTOTYPE( int blockdriver_handle_request, (struct blockdriver *bdp,
|
||||
message *m_ptr) );
|
||||
_PROTOTYPE( void blockdriver_reply, (message *m_ptr, int ipc_status,
|
||||
int reply) );
|
||||
|
||||
#endif /* _BLOCKDRIVER_DRIVER_H */
|
||||
@@ -1,17 +1,17 @@
|
||||
/* This file contains the multithreaded device independent driver interface.
|
||||
/* This file contains the multithreaded driver interface.
|
||||
*
|
||||
* Changes:
|
||||
* Aug 27, 2011 created (A. Welzel)
|
||||
*
|
||||
* The entry points into this file are:
|
||||
* driver_mt_task: the main message loop of the driver
|
||||
* driver_mt_terminate: break out of the main message loop
|
||||
* driver_mt_sleep: put the current thread to sleep
|
||||
* driver_mt_wakeup: wake up a sleeping thread
|
||||
* driver_mt_stop: put up the current thread for termination
|
||||
* blockdriver_mt_task: the main message loop of the driver
|
||||
* blockdriver_mt_terminate: break out of the main message loop
|
||||
* blockdriver_mt_sleep: put the current thread to sleep
|
||||
* blockdriver_mt_wakeup: wake up a sleeping thread
|
||||
* blockdriver_mt_stop: put up the current thread for termination
|
||||
*/
|
||||
|
||||
#include <minix/driver_mt.h>
|
||||
#include <minix/blockdriver_mt.h>
|
||||
#include <minix/mthread.h>
|
||||
#include <assert.h>
|
||||
|
||||
@@ -35,9 +35,8 @@ typedef struct {
|
||||
event_t sleep_event;
|
||||
} worker_t;
|
||||
|
||||
PRIVATE struct driver *driver_cb;
|
||||
PRIVATE int driver_mt_type;
|
||||
PRIVATE int driver_mt_running = FALSE;
|
||||
PRIVATE struct blockdriver *bdtab;
|
||||
PRIVATE int running = FALSE;
|
||||
|
||||
PRIVATE mthread_key_t worker_key;
|
||||
|
||||
@@ -57,10 +56,10 @@ PRIVATE void enqueue(worker_t *wp, const message *m_src, int ipc_status)
|
||||
|
||||
assert(wp->state == STATE_RUNNING || wp->state == STATE_STOPPING);
|
||||
|
||||
if (!driver_mq_enqueue(wp->id, m_src, ipc_status))
|
||||
panic("driver_mt: enqueue failed (message queue full)");
|
||||
if (!mq_enqueue(wp->id, m_src, ipc_status))
|
||||
panic("blockdriver_mt: enqueue failed (message queue full)");
|
||||
|
||||
driver_event_fire(&wp->queue_event);
|
||||
event_fire(&wp->queue_event);
|
||||
}
|
||||
|
||||
/*===========================================================================*
|
||||
@@ -73,7 +72,7 @@ PRIVATE int try_dequeue(worker_t *wp, message *m_dst, int *ipc_status)
|
||||
* called from a worker thread. Does not block.
|
||||
*/
|
||||
|
||||
return driver_mq_dequeue(wp->id, m_dst, ipc_status);
|
||||
return mq_dequeue(wp->id, m_dst, ipc_status);
|
||||
}
|
||||
|
||||
/*===========================================================================*
|
||||
@@ -86,7 +85,7 @@ PRIVATE void dequeue(worker_t *wp, message *m_dst, int *ipc_status)
|
||||
*/
|
||||
|
||||
while (!try_dequeue(wp, m_dst, ipc_status))
|
||||
driver_event_wait(&wp->queue_event);
|
||||
event_wait(&wp->queue_event);
|
||||
}
|
||||
|
||||
/*===========================================================================*
|
||||
@@ -102,14 +101,14 @@ PRIVATE void *worker_thread(void *param)
|
||||
worker_t *wp;
|
||||
message m;
|
||||
int ipc_status, r;
|
||||
|
||||
|
||||
wp = (worker_t *) param;
|
||||
assert(wp != NULL);
|
||||
|
||||
if (mthread_setspecific(worker_key, wp))
|
||||
panic("driver_mt: could not save local thread pointer");
|
||||
panic("blockdriver_mt: could not save local thread pointer");
|
||||
|
||||
while (driver_mt_running) {
|
||||
while (running) {
|
||||
/* See if a new message is available right away. */
|
||||
if (!try_dequeue(wp, &m, &ipc_status)) {
|
||||
/* If not, and this thread should be stopped, stop now. */
|
||||
@@ -119,7 +118,7 @@ PRIVATE void *worker_thread(void *param)
|
||||
/* Otherwise, block waiting for a new message. */
|
||||
dequeue(wp, &m, &ipc_status);
|
||||
|
||||
if (!driver_mt_running)
|
||||
if (!running)
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -127,14 +126,14 @@ PRIVATE void *worker_thread(void *param)
|
||||
wp->state = STATE_RUNNING;
|
||||
|
||||
/* Handle the request, and send a reply. */
|
||||
r = driver_handle_request(driver_cb, &m);
|
||||
r = blockdriver_handle_request(bdtab, &m);
|
||||
|
||||
driver_reply(driver_mt_type, &m, ipc_status, r);
|
||||
blockdriver_reply(&m, ipc_status, r);
|
||||
}
|
||||
|
||||
/* Clean up and terminate this thread. */
|
||||
if (mthread_setspecific(worker_key, NULL))
|
||||
panic("driver_mt: could not delete local thread pointer");
|
||||
panic("blockdriver_mt: could not delete local thread pointer");
|
||||
|
||||
wp->state = STATE_EXITED;
|
||||
|
||||
@@ -156,13 +155,13 @@ PRIVATE void master_create_worker(worker_t *wp, thread_id_t id)
|
||||
wp->state = STATE_RUNNING;
|
||||
|
||||
/* Initialize synchronization primitives. */
|
||||
driver_event_init(&wp->queue_event);
|
||||
driver_event_init(&wp->sleep_event);
|
||||
event_init(&wp->queue_event);
|
||||
event_init(&wp->sleep_event);
|
||||
|
||||
r = mthread_create(&wp->mthread, NULL /*attr*/, worker_thread, (void *) wp);
|
||||
|
||||
if (r != 0)
|
||||
panic("driver_mt: could not start thread %d (%d)", id, r);
|
||||
panic("blockdriver_mt: could not start thread %d (%d)", id, r);
|
||||
}
|
||||
|
||||
/*===========================================================================*
|
||||
@@ -177,15 +176,15 @@ PRIVATE void master_destroy_worker(worker_t *wp)
|
||||
|
||||
assert(wp != NULL);
|
||||
assert(wp->state == STATE_EXITED);
|
||||
assert(!driver_mq_dequeue(wp->id, &m, &ipc_status));
|
||||
assert(!mq_dequeue(wp->id, &m, &ipc_status));
|
||||
|
||||
/* Join the thread. */
|
||||
if (mthread_join(wp->mthread, NULL))
|
||||
panic("driver_mt: could not join thread %d", wp->id);
|
||||
panic("blockdriver_mt: could not join thread %d", wp->id);
|
||||
|
||||
/* Destroy resources. */
|
||||
driver_event_destroy(&wp->sleep_event);
|
||||
driver_event_destroy(&wp->queue_event);
|
||||
event_destroy(&wp->sleep_event);
|
||||
event_destroy(&wp->queue_event);
|
||||
|
||||
wp->state = STATE_DEAD;
|
||||
}
|
||||
@@ -218,26 +217,24 @@ PRIVATE void master_handle_request(message *m_ptr, int ipc_status)
|
||||
worker_t *wp;
|
||||
int r;
|
||||
|
||||
/* If this is not a request that has a minor device associated with it, we
|
||||
* can not tell which thread should process it either. In that case, the
|
||||
* master thread has to handle it instead.
|
||||
/* If this is not a block driver request, we cannot get the minor device
|
||||
* associated with it, and thus we can not tell which thread should process
|
||||
* it either. In that case, the master thread has to handle it instead.
|
||||
*/
|
||||
if (!IS_DEV_MINOR_RQ(m_ptr->m_type)) {
|
||||
if (driver_cb->dr_other)
|
||||
r = (*driver_cb->dr_other)(driver_cb, m_ptr);
|
||||
else
|
||||
r = EINVAL;
|
||||
if (!IS_BDEV_RQ(m_ptr->m_type)) {
|
||||
/* Process as 'other' message. */
|
||||
r = blockdriver_handle_request(bdtab, m_ptr);
|
||||
|
||||
driver_reply(driver_mt_type, m_ptr, ipc_status, r);
|
||||
blockdriver_reply(m_ptr, ipc_status, r);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
/* Query the thread ID. Upon failure, send the error code to the caller. */
|
||||
r = driver_cb->dr_thread(m_ptr->DEVICE, &thread_id);
|
||||
r = (*bdtab->bdr_thread)(m_ptr->DEVICE, &thread_id);
|
||||
|
||||
if (r != OK) {
|
||||
driver_reply(driver_mt_type, m_ptr, ipc_status, r);
|
||||
blockdriver_reply(m_ptr, ipc_status, r);
|
||||
|
||||
return;
|
||||
}
|
||||
@@ -259,19 +256,18 @@ PRIVATE void master_handle_request(message *m_ptr, int ipc_status)
|
||||
/*===========================================================================*
|
||||
* master_init *
|
||||
*===========================================================================*/
|
||||
PRIVATE void master_init(struct driver *dp, int type)
|
||||
PRIVATE void master_init(struct blockdriver *bdp)
|
||||
{
|
||||
/* Initialize the state of the master thread.
|
||||
*/
|
||||
int i;
|
||||
|
||||
assert(dp != NULL);
|
||||
assert(dp->dr_thread != NULL);
|
||||
assert(bdp != NULL);
|
||||
assert(bdp->bdr_thread != NULL);
|
||||
|
||||
mthread_init();
|
||||
|
||||
driver_mt_type = type;
|
||||
driver_cb = dp;
|
||||
bdtab = bdp;
|
||||
|
||||
for (i = 0; i < DRIVER_MT_MAX_WORKERS; i++)
|
||||
worker[i].state = STATE_DEAD;
|
||||
@@ -280,13 +276,13 @@ PRIVATE void master_init(struct driver *dp, int type)
|
||||
* reference to the worker structure.
|
||||
*/
|
||||
if (mthread_key_create(&worker_key, NULL))
|
||||
panic("driver_mt: error initializing worker key");
|
||||
panic("blockdriver_mt: error initializing worker key");
|
||||
}
|
||||
|
||||
/*===========================================================================*
|
||||
* driver_mt_receive *
|
||||
* blockdriver_mt_receive *
|
||||
*===========================================================================*/
|
||||
PRIVATE void driver_mt_receive(message *m_ptr, int *ipc_status)
|
||||
PRIVATE void blockdriver_mt_receive(message *m_ptr, int *ipc_status)
|
||||
{
|
||||
/* Receive a message.
|
||||
*/
|
||||
@@ -295,13 +291,13 @@ PRIVATE void driver_mt_receive(message *m_ptr, int *ipc_status)
|
||||
r = sef_receive_status(ANY, m_ptr, ipc_status);
|
||||
|
||||
if (r != OK)
|
||||
panic("driver_mt: sef_receive_status() returned %d", r);
|
||||
panic("blockdriver_mt: sef_receive_status() returned %d", r);
|
||||
}
|
||||
|
||||
/*===========================================================================*
|
||||
* driver_mt_task *
|
||||
* blockdriver_mt_task *
|
||||
*===========================================================================*/
|
||||
PUBLIC void driver_mt_task(struct driver *driver_p, int driver_type)
|
||||
PUBLIC void blockdriver_mt_task(struct blockdriver *driver_tab)
|
||||
{
|
||||
/* The multithreaded driver task.
|
||||
*/
|
||||
@@ -309,20 +305,20 @@ PUBLIC void driver_mt_task(struct driver *driver_p, int driver_type)
|
||||
message mess;
|
||||
|
||||
/* Initialize first if necessary. */
|
||||
if (!driver_mt_running) {
|
||||
master_init(driver_p, driver_type);
|
||||
if (!running) {
|
||||
master_init(driver_tab);
|
||||
|
||||
driver_mt_running = TRUE;
|
||||
running = TRUE;
|
||||
}
|
||||
|
||||
/* The main message loop. */
|
||||
while (driver_mt_running) {
|
||||
while (running) {
|
||||
/* Receive a message. */
|
||||
driver_mt_receive(&mess, &ipc_status);
|
||||
blockdriver_mt_receive(&mess, &ipc_status);
|
||||
|
||||
/* Dispatch the message. */
|
||||
if (is_ipc_notify(ipc_status))
|
||||
driver_handle_notify(driver_cb, &mess);
|
||||
blockdriver_handle_notify(bdtab, &mess);
|
||||
else
|
||||
master_handle_request(&mess, ipc_status);
|
||||
|
||||
@@ -336,20 +332,20 @@ PUBLIC void driver_mt_task(struct driver *driver_p, int driver_type)
|
||||
}
|
||||
|
||||
/*===========================================================================*
|
||||
* driver_mt_terminate *
|
||||
* blockdriver_mt_terminate *
|
||||
*===========================================================================*/
|
||||
PUBLIC void driver_mt_terminate(void)
|
||||
PUBLIC void blockdriver_mt_terminate(void)
|
||||
{
|
||||
/* Instruct libdriver to shut down.
|
||||
/* Instruct libblockdriver to shut down.
|
||||
*/
|
||||
|
||||
driver_mt_running = FALSE;
|
||||
running = FALSE;
|
||||
}
|
||||
|
||||
/*===========================================================================*
|
||||
* driver_mt_sleep *
|
||||
* blockdriver_mt_sleep *
|
||||
*===========================================================================*/
|
||||
PUBLIC void driver_mt_sleep(void)
|
||||
PUBLIC void blockdriver_mt_sleep(void)
|
||||
{
|
||||
/* Let the current thread sleep until it gets woken up by the master thread.
|
||||
*/
|
||||
@@ -358,15 +354,15 @@ PUBLIC void driver_mt_sleep(void)
|
||||
wp = (worker_t *) mthread_getspecific(worker_key);
|
||||
|
||||
if (wp == NULL)
|
||||
panic("driver_mt: master thread cannot sleep");
|
||||
panic("blockdriver_mt: master thread cannot sleep");
|
||||
|
||||
driver_event_wait(&wp->sleep_event);
|
||||
event_wait(&wp->sleep_event);
|
||||
}
|
||||
|
||||
/*===========================================================================*
|
||||
* driver_mt_wakeup *
|
||||
* blockdriver_mt_wakeup *
|
||||
*===========================================================================*/
|
||||
PUBLIC void driver_mt_wakeup(thread_id_t id)
|
||||
PUBLIC void blockdriver_mt_wakeup(thread_id_t id)
|
||||
{
|
||||
/* Wake up a sleeping worker thread from the master thread.
|
||||
*/
|
||||
@@ -378,13 +374,13 @@ PUBLIC void driver_mt_wakeup(thread_id_t id)
|
||||
|
||||
assert(wp->state == STATE_RUNNING || wp->state == STATE_STOPPING);
|
||||
|
||||
driver_event_fire(&wp->sleep_event);
|
||||
event_fire(&wp->sleep_event);
|
||||
}
|
||||
|
||||
/*===========================================================================*
|
||||
* driver_mt_stop *
|
||||
* blockdriver_mt_stop *
|
||||
*===========================================================================*/
|
||||
PUBLIC void driver_mt_stop(void)
|
||||
PUBLIC void blockdriver_mt_stop(void)
|
||||
{
|
||||
/* Put up the current worker thread for termination. Once the current dispatch
|
||||
* call has finished, and there are no more messages in the thread's message
|
||||
@@ -392,7 +388,7 @@ PUBLIC void driver_mt_stop(void)
|
||||
* the effect of this call.
|
||||
*/
|
||||
worker_t *wp;
|
||||
|
||||
|
||||
wp = (worker_t *) mthread_getspecific(worker_key);
|
||||
|
||||
assert(wp != NULL);
|
||||
98
lib/libblockdriver/driver_st.c
Normal file
98
lib/libblockdriver/driver_st.c
Normal file
@@ -0,0 +1,98 @@
|
||||
/* This file contains the singlethreaded device driver interface.
|
||||
*
|
||||
* Changes:
|
||||
* Aug 27, 2011 extracted from driver.c (A. Welzel)
|
||||
*
|
||||
* The entry points into this file are:
|
||||
* blockdriver_task: the main message loop of the driver
|
||||
* blockdriver_terminate: break out of the main message loop
|
||||
* blockdriver_handle_msg: handle a single received message
|
||||
* blockdriver_receive_mq: message receive interface with message queueing
|
||||
* blockdriver_mq_queue: queue an incoming message for later processing
|
||||
*/
|
||||
|
||||
#include <minix/drivers.h>
|
||||
#include <minix/blockdriver.h>
|
||||
|
||||
#include "driver.h"
|
||||
#include "mq.h"
|
||||
|
||||
PRIVATE int running;
|
||||
|
||||
/*===========================================================================*
|
||||
* blockdriver_receive_mq *
|
||||
*===========================================================================*/
|
||||
PUBLIC int blockdriver_receive_mq(message *m_ptr, int *status_ptr)
|
||||
{
|
||||
/* receive() interface for drivers with message queueing. */
|
||||
|
||||
/* Any queued messages? */
|
||||
if (mq_dequeue(MQ_SINGLE, m_ptr, status_ptr))
|
||||
return OK;
|
||||
|
||||
/* Fall back to standard receive() interface otherwise. */
|
||||
return driver_receive(ANY, m_ptr, status_ptr);
|
||||
}
|
||||
|
||||
/*===========================================================================*
|
||||
* blockdriver_terminate *
|
||||
*===========================================================================*/
|
||||
PUBLIC void blockdriver_terminate(void)
|
||||
{
|
||||
/* Break out of the main driver loop after finishing the current request. */
|
||||
|
||||
running = FALSE;
|
||||
}
|
||||
|
||||
/*===========================================================================*
|
||||
* blockdriver_task *
|
||||
*===========================================================================*/
|
||||
PUBLIC void blockdriver_task(struct blockdriver *bdp)
|
||||
{
|
||||
/* Main program of any block device driver task. */
|
||||
int r, ipc_status;
|
||||
message mess;
|
||||
|
||||
running = TRUE;
|
||||
|
||||
/* Here is the main loop of the disk task. It waits for a message, carries
|
||||
* it out, and sends a reply.
|
||||
*/
|
||||
while (running) {
|
||||
if ((r = blockdriver_receive_mq(&mess, &ipc_status)) != OK)
|
||||
panic("blockdriver_receive_mq failed: %d", r);
|
||||
|
||||
blockdriver_process(bdp, &mess, ipc_status);
|
||||
}
|
||||
}
|
||||
|
||||
/*===========================================================================*
|
||||
* blockdriver_process *
|
||||
*===========================================================================*/
|
||||
PUBLIC void blockdriver_process(struct blockdriver *bdp, message *m_ptr,
|
||||
int ipc_status)
|
||||
{
|
||||
/* Handle the given received message. */
|
||||
int r;
|
||||
|
||||
/* Process the notification or request. */
|
||||
if (is_ipc_notify(ipc_status)) {
|
||||
blockdriver_handle_notify(bdp, m_ptr);
|
||||
|
||||
/* Do not reply to notifications. */
|
||||
} else {
|
||||
r = blockdriver_handle_request(bdp, m_ptr);
|
||||
|
||||
blockdriver_reply(m_ptr, ipc_status, r);
|
||||
}
|
||||
}
|
||||
|
||||
/*===========================================================================*
|
||||
* blockdriver_mq_queue *
|
||||
*===========================================================================*/
|
||||
PUBLIC int blockdriver_mq_queue(message *m, int status)
|
||||
{
|
||||
/* Queue a message for later processing. */
|
||||
|
||||
return mq_enqueue(MQ_SINGLE, m, status);
|
||||
}
|
||||
@@ -4,36 +4,36 @@
|
||||
* partition: partition a disk to the partition table(s) on it.
|
||||
*/
|
||||
|
||||
#include <minix/driver.h>
|
||||
#include <minix/blockdriver.h>
|
||||
#include <minix/drvlib.h>
|
||||
#include <unistd.h>
|
||||
|
||||
/* Extended partition? */
|
||||
#define ext_part(s) ((s) == 0x05 || (s) == 0x0F)
|
||||
|
||||
FORWARD _PROTOTYPE( void parse_part_table, (struct driver *dp, int device,
|
||||
int style, int atapi, u8_t *tmp_buf) );
|
||||
FORWARD _PROTOTYPE( void extpartition, (struct driver *dp, int extdev,
|
||||
unsigned long extbase, u8_t *tmp_buf) );
|
||||
FORWARD _PROTOTYPE( int get_part_table, (struct driver *dp, int device,
|
||||
FORWARD _PROTOTYPE( void parse_part_table, (struct blockdriver *bdp,
|
||||
int device, int style, int atapi, u8_t *tmp_buf) );
|
||||
FORWARD _PROTOTYPE( void extpartition, (struct blockdriver *bdp, int extdev,
|
||||
unsigned long extbase, u8_t *tmp_buf) );
|
||||
FORWARD _PROTOTYPE( int get_part_table, (struct blockdriver *bdp, int device,
|
||||
unsigned long offset, struct part_entry *table, u8_t *tmp_buf) );
|
||||
FORWARD _PROTOTYPE( void sort, (struct part_entry *table) );
|
||||
|
||||
/*============================================================================*
|
||||
* partition *
|
||||
*============================================================================*/
|
||||
PUBLIC void partition(dp, device, style, atapi)
|
||||
struct driver *dp; /* device dependent entry points */
|
||||
int device; /* device to partition */
|
||||
int style; /* partitioning style: floppy, primary, sub. */
|
||||
int atapi; /* atapi device */
|
||||
PUBLIC void partition(bdp, device, style, atapi)
|
||||
struct blockdriver *bdp; /* device dependent entry points */
|
||||
int device; /* device to partition */
|
||||
int style; /* partitioning style: floppy, primary, sub. */
|
||||
int atapi; /* atapi device */
|
||||
{
|
||||
/* This routine is called on first open to initialize the partition tables
|
||||
* of a device.
|
||||
*/
|
||||
u8_t *tmp_buf;
|
||||
|
||||
if ((*dp->dr_prepare)(device) == NULL)
|
||||
if ((*bdp->bdr_part)(device) == NULL)
|
||||
return;
|
||||
|
||||
/* For multithreaded drivers, multiple partition() calls may be made on
|
||||
@@ -43,7 +43,7 @@ int atapi; /* atapi device */
|
||||
if (!(tmp_buf = alloc_contig(CD_SECTOR_SIZE, AC_ALIGN4K, NULL)))
|
||||
panic("partition: unable to allocate temporary buffer");
|
||||
|
||||
parse_part_table(dp, device, style, atapi, tmp_buf);
|
||||
parse_part_table(bdp, device, style, atapi, tmp_buf);
|
||||
|
||||
free_contig(tmp_buf, CD_SECTOR_SIZE);
|
||||
}
|
||||
@@ -51,12 +51,12 @@ int atapi; /* atapi device */
|
||||
/*============================================================================*
|
||||
* parse_part_table *
|
||||
*============================================================================*/
|
||||
PRIVATE void parse_part_table(dp, device, style, atapi, tmp_buf)
|
||||
struct driver *dp; /* device dependent entry points */
|
||||
int device; /* device to partition */
|
||||
int style; /* partitioning style: floppy, primary, sub. */
|
||||
int atapi; /* atapi device */
|
||||
u8_t *tmp_buf; /* temporary buffer */
|
||||
PRIVATE void parse_part_table(bdp, device, style, atapi, tmp_buf)
|
||||
struct blockdriver *bdp; /* device dependent entry points */
|
||||
int device; /* device to partition */
|
||||
int style; /* partitioning style: floppy, primary, sub. */
|
||||
int atapi; /* atapi device */
|
||||
u8_t *tmp_buf; /* temporary buffer */
|
||||
{
|
||||
/* This routine reads and parses a partition table. It may be called
|
||||
* recursively. It makes sure that each partition falls safely within the
|
||||
@@ -71,13 +71,13 @@ u8_t *tmp_buf; /* temporary buffer */
|
||||
unsigned long base, limit, part_limit;
|
||||
|
||||
/* Get the geometry of the device to partition */
|
||||
if ((dv = (*dp->dr_prepare)(device)) == NULL
|
||||
if ((dv = (*bdp->bdr_part)(device)) == NULL
|
||||
|| cmp64u(dv->dv_size, 0) == 0) return;
|
||||
base = div64u(dv->dv_base, SECTOR_SIZE);
|
||||
limit = base + div64u(dv->dv_size, SECTOR_SIZE);
|
||||
|
||||
/* Read the partition table for the device. */
|
||||
if(!get_part_table(dp, device, 0L, table, tmp_buf)) {
|
||||
if(!get_part_table(bdp, device, 0L, table, tmp_buf)) {
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -97,7 +97,7 @@ u8_t *tmp_buf; /* temporary buffer */
|
||||
}
|
||||
|
||||
/* Find an array of devices. */
|
||||
if ((dv = (*dp->dr_prepare)(device)) == NULL) return;
|
||||
if ((dv = (*bdp->bdr_part)(device)) == NULL) return;
|
||||
|
||||
/* Set the geometry of the partitions from the partition table. */
|
||||
for (par = 0; par < NR_PARTITIONS; par++, dv++) {
|
||||
@@ -115,12 +115,12 @@ u8_t *tmp_buf; /* temporary buffer */
|
||||
if (style == P_PRIMARY) {
|
||||
/* Each Minix primary partition can be subpartitioned. */
|
||||
if (pe->sysind == MINIX_PART)
|
||||
parse_part_table(dp, device + par, P_SUB, atapi,
|
||||
parse_part_table(bdp, device + par, P_SUB, atapi,
|
||||
tmp_buf);
|
||||
|
||||
/* An extended partition has logical partitions. */
|
||||
if (ext_part(pe->sysind))
|
||||
extpartition(dp, device + par, pe->lowsec, tmp_buf);
|
||||
extpartition(bdp, device + par, pe->lowsec, tmp_buf);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -128,11 +128,11 @@ u8_t *tmp_buf; /* temporary buffer */
|
||||
/*============================================================================*
|
||||
* extpartition *
|
||||
*============================================================================*/
|
||||
PRIVATE void extpartition(dp, extdev, extbase, tmp_buf)
|
||||
struct driver *dp; /* device dependent entry points */
|
||||
int extdev; /* extended partition to scan */
|
||||
unsigned long extbase; /* sector offset of the base extended partition */
|
||||
u8_t *tmp_buf; /* temporary buffer */
|
||||
PRIVATE void extpartition(bdp, extdev, extbase, tmp_buf)
|
||||
struct blockdriver *bdp; /* device dependent entry points */
|
||||
int extdev; /* extended partition to scan */
|
||||
unsigned long extbase; /* sector offset of the base ext. partition */
|
||||
u8_t *tmp_buf; /* temporary buffer */
|
||||
{
|
||||
/* Extended partitions cannot be ignored alas, because people like to move
|
||||
* files to and from DOS partitions. Avoid reading this code, it's no fun.
|
||||
@@ -148,7 +148,7 @@ u8_t *tmp_buf; /* temporary buffer */
|
||||
|
||||
offset = 0;
|
||||
do {
|
||||
if (!get_part_table(dp, extdev, offset, table, tmp_buf)) return;
|
||||
if (!get_part_table(bdp, extdev, offset, table, tmp_buf)) return;
|
||||
sort(table);
|
||||
|
||||
/* The table should contain one logical partition and optionally
|
||||
@@ -161,7 +161,7 @@ u8_t *tmp_buf; /* temporary buffer */
|
||||
nextoffset = pe->lowsec;
|
||||
} else
|
||||
if (pe->sysind != NO_PART) {
|
||||
if ((dv = (*dp->dr_prepare)(subdev)) == NULL) return;
|
||||
if ((dv = (*bdp->bdr_part)(subdev)) == NULL) return;
|
||||
|
||||
dv->dv_base = mul64u(extbase + offset + pe->lowsec,
|
||||
SECTOR_SIZE);
|
||||
@@ -177,8 +177,8 @@ u8_t *tmp_buf; /* temporary buffer */
|
||||
/*============================================================================*
|
||||
* get_part_table *
|
||||
*============================================================================*/
|
||||
PRIVATE int get_part_table(dp, device, offset, table, tmp_buf)
|
||||
struct driver *dp;
|
||||
PRIVATE int get_part_table(bdp, device, offset, table, tmp_buf)
|
||||
struct blockdriver *bdp;
|
||||
int device;
|
||||
unsigned long offset; /* sector offset to the table */
|
||||
struct part_entry *table; /* four entries */
|
||||
@@ -189,14 +189,14 @@ u8_t *tmp_buf; /* temporary buffer */
|
||||
*/
|
||||
iovec_t iovec1;
|
||||
u64_t position;
|
||||
int r;
|
||||
|
||||
position = mul64u(offset, SECTOR_SIZE);
|
||||
iovec1.iov_addr = (vir_bytes) tmp_buf;
|
||||
iovec1.iov_size = CD_SECTOR_SIZE;
|
||||
if ((*dp->dr_prepare)(device) != NULL) {
|
||||
(void) (*dp->dr_transfer)(SELF, DEV_GATHER_S, position, &iovec1, 1);
|
||||
}
|
||||
if (iovec1.iov_size != 0) {
|
||||
r = (*bdp->bdr_transfer)(device, FALSE /*do_write*/, position, SELF,
|
||||
&iovec1, 1, BDEV_NOFLAGS);
|
||||
if (r != CD_SECTOR_SIZE) {
|
||||
return 0;
|
||||
}
|
||||
if (tmp_buf[510] != 0x55 || tmp_buf[511] != 0xAA) {
|
||||
@@ -7,65 +7,65 @@
|
||||
#include "event.h"
|
||||
|
||||
/*===========================================================================*
|
||||
* driver_event_init *
|
||||
* event_init *
|
||||
*===========================================================================*/
|
||||
PUBLIC void driver_event_init(event_t *event)
|
||||
PUBLIC void event_init(event_t *event)
|
||||
{
|
||||
/* Initialize an event object.
|
||||
*/
|
||||
int r;
|
||||
|
||||
if ((r = mthread_mutex_init(&event->mutex, NULL)) != 0)
|
||||
panic("libdriver: error initializing mutex (%d)", r);
|
||||
panic("libblockdriver: error initializing mutex (%d)", r);
|
||||
if ((r = mthread_cond_init(&event->cond, NULL)) != 0)
|
||||
panic("libdriver: error initializing condvar (%d)", r);
|
||||
panic("libblockdriver: error initializing condvar (%d)", r);
|
||||
}
|
||||
|
||||
/*===========================================================================*
|
||||
* driver_event_destroy *
|
||||
* event_destroy *
|
||||
*===========================================================================*/
|
||||
PUBLIC void driver_event_destroy(event_t *event)
|
||||
PUBLIC void event_destroy(event_t *event)
|
||||
{
|
||||
/* Destroy an event object.
|
||||
*/
|
||||
int r;
|
||||
|
||||
if ((r = mthread_cond_destroy(&event->cond)) != 0)
|
||||
panic("libdriver: error destroying condvar (%d)", r);
|
||||
panic("libblockdriver: error destroying condvar (%d)", r);
|
||||
if ((r = mthread_mutex_destroy(&event->mutex)) != 0)
|
||||
panic("libdriver: error destroying mutex (%d)", r);
|
||||
panic("libblockdriver: error destroying mutex (%d)", r);
|
||||
}
|
||||
|
||||
/*===========================================================================*
|
||||
* driver_event_wait *
|
||||
* event_wait *
|
||||
*===========================================================================*/
|
||||
PUBLIC void driver_event_wait(event_t *event)
|
||||
PUBLIC void event_wait(event_t *event)
|
||||
{
|
||||
/* Wait for an event, blocking the current thread in the process.
|
||||
*/
|
||||
int r;
|
||||
|
||||
if ((r = mthread_mutex_lock(&event->mutex)) != 0)
|
||||
panic("libdriver: error locking mutex (%d)", r);
|
||||
panic("libblockdriver: error locking mutex (%d)", r);
|
||||
if ((r = mthread_cond_wait(&event->cond, &event->mutex)) != 0)
|
||||
panic("libdriver: error waiting for condvar (%d)", r);
|
||||
panic("libblockdriver: error waiting for condvar (%d)", r);
|
||||
if ((r = mthread_mutex_unlock(&event->mutex)) != 0)
|
||||
panic("libdriver: error unlocking mutex (%d)", r);
|
||||
panic("libblockdriver: error unlocking mutex (%d)", r);
|
||||
}
|
||||
|
||||
/*===========================================================================*
|
||||
* driver_event_fire *
|
||||
* event_fire *
|
||||
*===========================================================================*/
|
||||
PUBLIC void driver_event_fire(event_t *event)
|
||||
PUBLIC void event_fire(event_t *event)
|
||||
{
|
||||
/* Fire an event, waking up any thread blocked on it without scheduling it.
|
||||
*/
|
||||
int r;
|
||||
|
||||
if ((r = mthread_mutex_lock(&event->mutex)) != 0)
|
||||
panic("libdriver: error locking mutex (%d)", r);
|
||||
panic("libblockdriver: error locking mutex (%d)", r);
|
||||
if ((r = mthread_cond_signal(&event->cond)) != 0)
|
||||
panic("libdriver: error signaling condvar (%d)", r);
|
||||
panic("libblockdriver: error signaling condvar (%d)", r);
|
||||
if ((r = mthread_mutex_unlock(&event->mutex)) != 0)
|
||||
panic("libdriver: error unlocking mutex (%d)", r);
|
||||
panic("libblockdriver: error unlocking mutex (%d)", r);
|
||||
}
|
||||
14
lib/libblockdriver/event.h
Normal file
14
lib/libblockdriver/event.h
Normal file
@@ -0,0 +1,14 @@
|
||||
#ifndef _BLOCKDRIVER_EVENT_H
|
||||
#define _BLOCKDRIVER_EVENT_H
|
||||
|
||||
typedef struct {
|
||||
mthread_mutex_t mutex;
|
||||
mthread_cond_t cond;
|
||||
} event_t;
|
||||
|
||||
_PROTOTYPE( void event_init, (event_t *event) );
|
||||
_PROTOTYPE( void event_destroy, (event_t *event) );
|
||||
_PROTOTYPE( void event_wait, (event_t *event) );
|
||||
_PROTOTYPE( void event_fire, (event_t *event) );
|
||||
|
||||
#endif /* _BLOCKDRIVER_EVENT_H */
|
||||
@@ -3,10 +3,10 @@
|
||||
*
|
||||
* Changes:
|
||||
* Oct 27, 2011 rewritten to use sys/queue.h (D.C. van Moolenbroek)
|
||||
* Aug 27, 2011 integrated into libdriver (A. Welzel)
|
||||
* Aug 27, 2011 integrated into libblockdriver (A. Welzel)
|
||||
*/
|
||||
|
||||
#include <minix/driver_mt.h>
|
||||
#include <minix/blockdriver_mt.h>
|
||||
#include <sys/queue.h>
|
||||
#include <assert.h>
|
||||
|
||||
@@ -26,11 +26,11 @@ PRIVATE STAILQ_HEAD(queue, mq_cell) queue[DRIVER_MT_MAX_WORKERS];
|
||||
PRIVATE STAILQ_HEAD(free_list, mq_cell) free_list;
|
||||
|
||||
/*===========================================================================*
|
||||
* driver_mq_init *
|
||||
* mq_init *
|
||||
*===========================================================================*/
|
||||
PUBLIC void driver_mq_init(void)
|
||||
PUBLIC void mq_init(void)
|
||||
{
|
||||
/* Initialize the message queues and message cells.
|
||||
/* Initialize the message queues and message cells.
|
||||
*/
|
||||
int i;
|
||||
|
||||
@@ -44,9 +44,9 @@ PUBLIC void driver_mq_init(void)
|
||||
}
|
||||
|
||||
/*===========================================================================*
|
||||
* driver_mq_enqueue *
|
||||
* mq_enqueue *
|
||||
*===========================================================================*/
|
||||
PUBLIC int driver_mq_enqueue(thread_id_t thread_id, const message *mess,
|
||||
PUBLIC int mq_enqueue(thread_id_t thread_id, const message *mess,
|
||||
int ipc_status)
|
||||
{
|
||||
/* Add a message, including its IPC status, to the message queue of a thread.
|
||||
@@ -71,10 +71,9 @@ PUBLIC int driver_mq_enqueue(thread_id_t thread_id, const message *mess,
|
||||
}
|
||||
|
||||
/*===========================================================================*
|
||||
* driver_mq_dequeue *
|
||||
* mq_dequeue *
|
||||
*===========================================================================*/
|
||||
PUBLIC int driver_mq_dequeue(thread_id_t thread_id, message *mess,
|
||||
int *ipc_status)
|
||||
PUBLIC int mq_dequeue(thread_id_t thread_id, message *mess, int *ipc_status)
|
||||
{
|
||||
/* Return and remove a message, including its IPC status, from the message
|
||||
* queue of a thread. Return TRUE iff a message was available.
|
||||
12
lib/libblockdriver/mq.h
Normal file
12
lib/libblockdriver/mq.h
Normal file
@@ -0,0 +1,12 @@
|
||||
#ifndef _BLOCKDRIVER_MQ_H
|
||||
#define _BLOCKDRIVER_MQ_H
|
||||
|
||||
#define MQ_SINGLE 0 /* thread ID for single-threading */
|
||||
|
||||
_PROTOTYPE( void mq_init, (void) );
|
||||
_PROTOTYPE( int mq_enqueue, (thread_id_t thread_id, const message *mess,
|
||||
int ipc_status) );
|
||||
_PROTOTYPE( int mq_dequeue, (thread_id_t thread_id, message *mess,
|
||||
int *ipc_status) );
|
||||
|
||||
#endif /* _BLOCKDRIVER_MQ_H */
|
||||
12
lib/libchardriver/Makefile
Normal file
12
lib/libchardriver/Makefile
Normal file
@@ -0,0 +1,12 @@
|
||||
# Makefile for libchardriver
|
||||
.include <bsd.own.mk>
|
||||
|
||||
LIB= chardriver
|
||||
|
||||
SRCS= chardriver.c
|
||||
|
||||
.if ${USE_STATECTL} != "no"
|
||||
CPPFLAGS+= -DUSE_STATECTL
|
||||
.endif
|
||||
|
||||
.include <bsd.lib.mk>
|
||||
@@ -1,6 +1,4 @@
|
||||
/* This file contains the common part of the device driver interface.
|
||||
* In addition, callers get to choose between the singlethreaded API
|
||||
* (in driver_st.c) and the multithreaded API (in driver_mt.c).
|
||||
/* This file contains the device independent character driver interface.
|
||||
*
|
||||
* The drivers support the following operations (using message format m2):
|
||||
*
|
||||
@@ -25,8 +23,13 @@
|
||||
* | 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:
|
||||
* Aug 27, 2011 split common functions into driver_common.c (A. Welzel)
|
||||
* 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)
|
||||
* Sep 15, 2004 added SYN_ALARM type for timeouts (Jorrit N. Herder)
|
||||
* Jul 23, 2004 removed kernel dependencies (Jorrit N. Herder)
|
||||
@@ -34,12 +37,10 @@
|
||||
*/
|
||||
|
||||
#include <minix/drivers.h>
|
||||
#include <sys/ioc_disk.h>
|
||||
#include <minix/driver.h>
|
||||
#include <minix/chardriver.h>
|
||||
#include <minix/ds.h>
|
||||
|
||||
#include "driver.h"
|
||||
#include "mq.h"
|
||||
PRIVATE int running;
|
||||
|
||||
/* Management data for opened devices. */
|
||||
PRIVATE int open_devs[MAX_NR_OPEN_DEVICES];
|
||||
@@ -84,9 +85,41 @@ PRIVATE void set_open_dev(int device)
|
||||
}
|
||||
|
||||
/*===========================================================================*
|
||||
* asyn_reply *
|
||||
* chardriver_announce *
|
||||
*===========================================================================*/
|
||||
PRIVATE void asyn_reply(message *mess, int r)
|
||||
PUBLIC void chardriver_announce(void)
|
||||
{
|
||||
/* Announce we are up after a fresh start or restart. */
|
||||
int r;
|
||||
char key[DS_MAX_KEYLEN];
|
||||
char label[DS_MAX_KEYLEN];
|
||||
char *driver_prefix = "drv.chr.";
|
||||
|
||||
/* Callers are allowed to use sendrec to communicate with drivers.
|
||||
* For this reason, there may blocked callers when a driver restarts.
|
||||
* Ask the kernel to unblock them (if any).
|
||||
*/
|
||||
#if USE_STATECTL
|
||||
if ((r = sys_statectl(SYS_STATE_CLEAR_IPC_REFS)) != OK)
|
||||
panic("chardriver_init: sys_statectl failed: %d", r);
|
||||
#endif
|
||||
|
||||
/* Publish a driver up event. */
|
||||
if ((r = ds_retrieve_label_name(label, getprocnr())) != OK)
|
||||
panic("chardriver_init: unable to get own label: %d", r);
|
||||
|
||||
snprintf(key, DS_MAX_KEYLEN, "%s%s", driver_prefix, label);
|
||||
if ((r = ds_publish_u32(key, DS_DRIVER_UP, DSF_OVERWRITE)) != OK)
|
||||
panic("chardriver_init: unable to publish driver up event: %d", r);
|
||||
|
||||
/* Expect a DEV_OPEN for any device before serving regular driver requests. */
|
||||
clear_open_devs();
|
||||
}
|
||||
|
||||
/*===========================================================================*
|
||||
* async_reply *
|
||||
*===========================================================================*/
|
||||
PRIVATE void async_reply(message *mess, int r)
|
||||
{
|
||||
/* Send a reply using the asynchronous character device protocol. */
|
||||
message reply_mess;
|
||||
@@ -150,9 +183,9 @@ PRIVATE void asyn_reply(message *mess, int r)
|
||||
}
|
||||
|
||||
/*===========================================================================*
|
||||
* standard_reply *
|
||||
* sync_reply *
|
||||
*===========================================================================*/
|
||||
PRIVATE void standard_reply(message *m_ptr, int ipc_status, int reply)
|
||||
PRIVATE void sync_reply(message *m_ptr, int ipc_status, int reply)
|
||||
{
|
||||
/* Reply to a message sent to the driver. */
|
||||
endpoint_t caller_e, user_e;
|
||||
@@ -176,75 +209,25 @@ PRIVATE void standard_reply(message *m_ptr, int ipc_status, int reply)
|
||||
}
|
||||
|
||||
/*===========================================================================*
|
||||
* driver_reply *
|
||||
* send_reply *
|
||||
*===========================================================================*/
|
||||
PUBLIC void driver_reply(int driver_type, message *m_ptr, int ipc_status,
|
||||
int reply)
|
||||
PRIVATE void send_reply(int type, message *m_ptr, int ipc_status, int reply)
|
||||
{
|
||||
/* Prepare and send a reply message. */
|
||||
|
||||
if (reply == EDONTREPLY)
|
||||
return;
|
||||
|
||||
switch (driver_type) {
|
||||
case DRIVER_STD:
|
||||
standard_reply(m_ptr, ipc_status, reply);
|
||||
|
||||
break;
|
||||
|
||||
case DRIVER_ASYN:
|
||||
asyn_reply(m_ptr, reply);
|
||||
|
||||
break;
|
||||
|
||||
default:
|
||||
panic("unknown driver type: %d", driver_type);
|
||||
}
|
||||
}
|
||||
|
||||
/*===========================================================================*
|
||||
* driver_announce *
|
||||
*===========================================================================*/
|
||||
PUBLIC void driver_announce(void)
|
||||
{
|
||||
/* Announce we are up after a fresh start or restart. */
|
||||
int r;
|
||||
char key[DS_MAX_KEYLEN];
|
||||
char label[DS_MAX_KEYLEN];
|
||||
char *driver_prefix = "drv.vfs.";
|
||||
|
||||
/* Callers are allowed to use sendrec to communicate with drivers.
|
||||
* For this reason, there may blocked callers when a driver restarts.
|
||||
* Ask the kernel to unblock them (if any).
|
||||
*/
|
||||
#if USE_STATECTL
|
||||
r = sys_statectl(SYS_STATE_CLEAR_IPC_REFS);
|
||||
if (r != OK) {
|
||||
panic("driver_announce: sys_statectl failed: %d\n", r);
|
||||
}
|
||||
#endif
|
||||
|
||||
/* Publish a driver up event. */
|
||||
r = ds_retrieve_label_name(label, getprocnr());
|
||||
if (r != OK) {
|
||||
panic("driver_announce: unable to get own label: %d\n", r);
|
||||
}
|
||||
snprintf(key, DS_MAX_KEYLEN, "%s%s", driver_prefix, label);
|
||||
r = ds_publish_u32(key, DS_DRIVER_UP, DSF_OVERWRITE);
|
||||
if (r != OK) {
|
||||
panic("driver_announce: unable to publish driver up event: %d\n", r);
|
||||
}
|
||||
|
||||
/* Expect a DEV_OPEN for any device before serving regular driver requests. */
|
||||
clear_open_devs();
|
||||
|
||||
driver_mq_init();
|
||||
if (type == CHARDRIVER_ASYNC)
|
||||
async_reply(m_ptr, reply);
|
||||
else
|
||||
sync_reply(m_ptr, ipc_status, reply);
|
||||
}
|
||||
|
||||
/*===========================================================================*
|
||||
* do_rdwt *
|
||||
*===========================================================================*/
|
||||
PRIVATE int do_rdwt(struct driver *dp, message *mp)
|
||||
PRIVATE int do_rdwt(struct chardriver *cdp, message *mp)
|
||||
{
|
||||
/* Carry out a single read or write request. */
|
||||
iovec_t iovec1;
|
||||
@@ -255,7 +238,7 @@ PRIVATE int do_rdwt(struct driver *dp, message *mp)
|
||||
if (mp->COUNT < 0) return(EINVAL);
|
||||
|
||||
/* Prepare for I/O. */
|
||||
if ((*dp->dr_prepare)(mp->DEVICE) == NULL) return(ENXIO);
|
||||
if ((*cdp->cdr_prepare)(mp->DEVICE) == NULL) return(ENXIO);
|
||||
|
||||
/* Create a one element scatter/gather vector for the buffer. */
|
||||
if(mp->m_type == DEV_READ_S) opcode = DEV_GATHER_S;
|
||||
@@ -266,7 +249,8 @@ PRIVATE int do_rdwt(struct driver *dp, message *mp)
|
||||
|
||||
/* Transfer bytes from/to the device. */
|
||||
position= make64(mp->POSITION, mp->HIGHPOS);
|
||||
r = (*dp->dr_transfer)(mp->m_source, opcode, position, &iovec1, 1);
|
||||
r = (*cdp->cdr_transfer)(mp->m_source, opcode, position, &iovec1, 1,
|
||||
mp->USER_ENDPT);
|
||||
|
||||
/* Return the number of bytes transferred or an error code. */
|
||||
return(r == OK ? (int) (mp->COUNT - iovec1.iov_size) : r);
|
||||
@@ -275,7 +259,7 @@ PRIVATE int do_rdwt(struct driver *dp, message *mp)
|
||||
/*===========================================================================*
|
||||
* do_vrdwt *
|
||||
*===========================================================================*/
|
||||
PRIVATE int do_vrdwt(struct driver *dp, message *mp)
|
||||
PRIVATE 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
|
||||
@@ -293,22 +277,23 @@ PRIVATE int do_vrdwt(struct driver *dp, message *mp)
|
||||
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,
|
||||
if (OK != sys_safecopyfrom(mp->m_source, (vir_bytes) mp->IO_GRANT,
|
||||
0, (vir_bytes) iovec, iovec_size, D)) {
|
||||
printf("bad I/O vector by: %d\n", mp->m_source);
|
||||
return(EINVAL);
|
||||
}
|
||||
|
||||
/* Prepare for I/O. */
|
||||
if ((*dp->dr_prepare)(mp->DEVICE) == NULL) return(ENXIO);
|
||||
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 = (*dp->dr_transfer)(mp->m_source, opcode, position, iovec, nr_req);
|
||||
r = (*cdp->cdr_transfer)(mp->m_source, opcode, position, iovec, nr_req,
|
||||
mp->USER_ENDPT);
|
||||
|
||||
/* Copy the I/O vector back to the caller. */
|
||||
if (OK != sys_safecopyto(mp->m_source, (vir_bytes) mp->IO_GRANT,
|
||||
if (OK != sys_safecopyto(mp->m_source, (vir_bytes) mp->IO_GRANT,
|
||||
0, (vir_bytes) iovec, iovec_size, D)) {
|
||||
printf("couldn't return I/O vector: %d\n", mp->m_source);
|
||||
return(EINVAL);
|
||||
@@ -318,9 +303,9 @@ PRIVATE int do_vrdwt(struct driver *dp, message *mp)
|
||||
}
|
||||
|
||||
/*===========================================================================*
|
||||
* driver_handle_notify *
|
||||
* handle_notify *
|
||||
*===========================================================================*/
|
||||
PUBLIC void driver_handle_notify(struct driver *dp, message *m_ptr)
|
||||
PRIVATE 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.
|
||||
@@ -328,26 +313,21 @@ PUBLIC void driver_handle_notify(struct driver *dp, message *m_ptr)
|
||||
|
||||
/* Call the appropriate function for this notification. */
|
||||
switch (_ENDPOINT_P(m_ptr->m_source)) {
|
||||
case HARDWARE:
|
||||
if (dp->dr_hw_int)
|
||||
dp->dr_hw_int(dp, m_ptr);
|
||||
break;
|
||||
|
||||
case CLOCK:
|
||||
if (dp->dr_alarm)
|
||||
dp->dr_alarm(dp, m_ptr);
|
||||
if (cdp->cdr_alarm)
|
||||
cdp->cdr_alarm(m_ptr);
|
||||
break;
|
||||
|
||||
default:
|
||||
if (dp->dr_other)
|
||||
(void) dp->dr_other(dp, m_ptr);
|
||||
if (cdp->cdr_other)
|
||||
(void) cdp->cdr_other(m_ptr);
|
||||
}
|
||||
}
|
||||
|
||||
/*===========================================================================*
|
||||
* driver_handle_request *
|
||||
* handle_request *
|
||||
*===========================================================================*/
|
||||
PUBLIC int driver_handle_request(struct driver *dp, message *m_ptr)
|
||||
PRIVATE 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
|
||||
@@ -359,7 +339,7 @@ PUBLIC int driver_handle_request(struct driver *dp, message *m_ptr)
|
||||
* requests on devices that have not previously been opened, signaling the
|
||||
* caller that something went wrong.
|
||||
*/
|
||||
if (IS_DEV_MINOR_RQ(m_ptr->m_type) && !is_open_dev(m_ptr->DEVICE)) {
|
||||
if (IS_CDEV_MINOR_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;
|
||||
@@ -370,63 +350,84 @@ PUBLIC int driver_handle_request(struct driver *dp, message *m_ptr)
|
||||
|
||||
/* Call the appropriate function(s) for this request. */
|
||||
switch (m_ptr->m_type) {
|
||||
case DEV_OPEN: r = (*dp->dr_open)(dp, m_ptr); break;
|
||||
case DEV_CLOSE: r = (*dp->dr_close)(dp, m_ptr); break;
|
||||
case DEV_IOCTL_S: r = (*dp->dr_ioctl)(dp, m_ptr); break;
|
||||
case CANCEL: r = (*dp->dr_cancel)(dp, m_ptr); break;
|
||||
case DEV_SELECT: r = (*dp->dr_select)(dp, m_ptr); break;
|
||||
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(dp, m_ptr); break;
|
||||
case DEV_WRITE_S: r = do_rdwt(cdp, m_ptr); break;
|
||||
case DEV_GATHER_S:
|
||||
case DEV_SCATTER_S: r = do_vrdwt(dp, m_ptr); break;
|
||||
case DEV_SCATTER_S: r = do_vrdwt(cdp, m_ptr); break;
|
||||
default:
|
||||
if (dp->dr_other)
|
||||
r = dp->dr_other(dp, m_ptr);
|
||||
if (cdp->cdr_other)
|
||||
r = cdp->cdr_other(m_ptr);
|
||||
else
|
||||
r = EINVAL;
|
||||
}
|
||||
|
||||
/* Let the driver perform any cleanup. */
|
||||
(*dp->dr_cleanup)();
|
||||
if (cdp->cdr_cleanup)
|
||||
(*cdp->cdr_cleanup)();
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
/*===========================================================================*
|
||||
* no_name *
|
||||
* chardriver_process *
|
||||
*===========================================================================*/
|
||||
PUBLIC char *no_name(void)
|
||||
PUBLIC void chardriver_process(struct chardriver *cdp, int driver_type,
|
||||
message *m_ptr, int ipc_status)
|
||||
{
|
||||
/* Use this default name if there is no specific name for the device. This was
|
||||
* originally done by fetching the name from the task table for this process:
|
||||
* "return(tasktab[proc_number(proc_ptr) + NR_TASKS].name);", but currently a
|
||||
* real "noname" is returned. Perhaps, some system information service can be
|
||||
* queried for a name at a later time.
|
||||
*/
|
||||
static char name[] = "noname";
|
||||
return name;
|
||||
/* Handle the given received message. */
|
||||
int r;
|
||||
|
||||
/* Process the notification or request. */
|
||||
if (is_ipc_notify(ipc_status)) {
|
||||
handle_notify(cdp, m_ptr);
|
||||
|
||||
/* Do not reply to notifications. */
|
||||
} else {
|
||||
r = handle_request(cdp, m_ptr);
|
||||
|
||||
send_reply(driver_type, m_ptr, ipc_status, r);
|
||||
}
|
||||
}
|
||||
|
||||
/*===========================================================================*
|
||||
* chardriver_task *
|
||||
*===========================================================================*/
|
||||
PUBLIC void chardriver_task(struct chardriver *cdp, int driver_type)
|
||||
{
|
||||
/* Main program of any device driver task. */
|
||||
int r, ipc_status;
|
||||
message mess;
|
||||
|
||||
running = TRUE;
|
||||
|
||||
/* Here is the main loop of the disk task. It waits for a message, carries
|
||||
* it out, and sends a reply.
|
||||
*/
|
||||
while (running) {
|
||||
if ((r = sef_receive_status(ANY, &mess, &ipc_status)) != OK)
|
||||
panic("driver_receive failed: %d", r);
|
||||
|
||||
chardriver_process(cdp, driver_type, &mess, ipc_status);
|
||||
}
|
||||
}
|
||||
|
||||
/*===========================================================================*
|
||||
* do_nop *
|
||||
*===========================================================================*/
|
||||
PUBLIC int do_nop(struct driver *UNUSED(dp), message *mp)
|
||||
PUBLIC int do_nop(message *UNUSED(mp))
|
||||
{
|
||||
/* Nothing there, or nothing to do. */
|
||||
|
||||
switch (mp->m_type) {
|
||||
case DEV_OPEN: return(ENODEV);
|
||||
case DEV_CLOSE: return(OK);
|
||||
case DEV_IOCTL_S:
|
||||
default: printf("nop: ignoring code %d\n", mp->m_type);
|
||||
return(EIO);
|
||||
}
|
||||
return(OK);
|
||||
}
|
||||
|
||||
/*===========================================================================*
|
||||
* nop_ioctl *
|
||||
*===========================================================================*/
|
||||
PUBLIC int nop_ioctl(struct driver *UNUSED(dp), message *UNUSED(mp))
|
||||
PUBLIC int nop_ioctl(message *UNUSED(mp))
|
||||
{
|
||||
return(ENOTTY);
|
||||
}
|
||||
@@ -434,20 +435,11 @@ PUBLIC int nop_ioctl(struct driver *UNUSED(dp), message *UNUSED(mp))
|
||||
/*===========================================================================*
|
||||
* nop_alarm *
|
||||
*===========================================================================*/
|
||||
PUBLIC void nop_alarm(struct driver *UNUSED(dp), message *UNUSED(mp))
|
||||
PUBLIC void nop_alarm(message *UNUSED(mp))
|
||||
{
|
||||
/* Ignore the leftover alarm. */
|
||||
}
|
||||
|
||||
/*===========================================================================*
|
||||
* nop_prepare *
|
||||
*===========================================================================*/
|
||||
PUBLIC struct device *nop_prepare(int UNUSED(device))
|
||||
{
|
||||
/* Nothing to prepare for. */
|
||||
return(NULL);
|
||||
}
|
||||
|
||||
/*===========================================================================*
|
||||
* nop_cleanup *
|
||||
*===========================================================================*/
|
||||
@@ -459,7 +451,7 @@ PUBLIC void nop_cleanup(void)
|
||||
/*===========================================================================*
|
||||
* nop_cancel *
|
||||
*===========================================================================*/
|
||||
PUBLIC int nop_cancel(struct driver *UNUSED(dr), message *UNUSED(m))
|
||||
PUBLIC int nop_cancel(message *UNUSED(m))
|
||||
{
|
||||
/* Nothing to do for cancel. */
|
||||
return(OK);
|
||||
@@ -468,52 +460,8 @@ PUBLIC int nop_cancel(struct driver *UNUSED(dr), message *UNUSED(m))
|
||||
/*===========================================================================*
|
||||
* nop_select *
|
||||
*===========================================================================*/
|
||||
PUBLIC int nop_select(struct driver *UNUSED(dr), message *UNUSED(m))
|
||||
PUBLIC int nop_select(message *UNUSED(m))
|
||||
{
|
||||
/* Nothing to do for select. */
|
||||
return(OK);
|
||||
}
|
||||
|
||||
/*===========================================================================*
|
||||
* do_diocntl *
|
||||
*===========================================================================*/
|
||||
PUBLIC int do_diocntl(struct driver *dp, message *mp)
|
||||
{
|
||||
/* Carry out a partition setting/getting request. */
|
||||
struct device *dv;
|
||||
struct partition entry;
|
||||
unsigned int request;
|
||||
int s;
|
||||
|
||||
request = mp->REQUEST;
|
||||
|
||||
if (request != DIOCSETP && request != DIOCGETP) {
|
||||
if(dp->dr_other)
|
||||
return dp->dr_other(dp, mp);
|
||||
else
|
||||
return(ENOTTY);
|
||||
}
|
||||
|
||||
/* Decode the message parameters. */
|
||||
if ((dv = (*dp->dr_prepare)(mp->DEVICE)) == NULL) return(ENXIO);
|
||||
|
||||
if (request == DIOCSETP) {
|
||||
/* Copy just this one partition table entry. */
|
||||
s=sys_safecopyfrom(mp->m_source, (vir_bytes) mp->IO_GRANT,
|
||||
0, (vir_bytes) &entry, sizeof(entry), D);
|
||||
if(s != OK)
|
||||
return s;
|
||||
dv->dv_base = entry.base;
|
||||
dv->dv_size = entry.size;
|
||||
} else {
|
||||
/* Return a partition table entry and the geometry of the drive. */
|
||||
entry.base = dv->dv_base;
|
||||
entry.size = dv->dv_size;
|
||||
(*dp->dr_geometry)(&entry);
|
||||
s=sys_safecopyto(mp->m_source, (vir_bytes) mp->IO_GRANT,
|
||||
0, (vir_bytes) &entry, sizeof(entry), D);
|
||||
if (OK != s)
|
||||
return s;
|
||||
}
|
||||
return(OK);
|
||||
}
|
||||
@@ -1,9 +0,0 @@
|
||||
#ifndef _DRIVER_DRIVER_H
|
||||
#define _DRIVER_DRIVER_H
|
||||
|
||||
_PROTOTYPE( void driver_handle_notify, (struct driver *dp, message *m_ptr) );
|
||||
_PROTOTYPE( int driver_handle_request, (struct driver *dp, message *m_ptr) );
|
||||
_PROTOTYPE( void driver_reply, (int driver_type, message *m_ptr,
|
||||
int ipc_status, int reply) );
|
||||
|
||||
#endif /* _DRIVER_DRIVER_H */
|
||||
@@ -1,115 +0,0 @@
|
||||
/* This file contains the singlethreaded device independent driver interface.
|
||||
*
|
||||
* Changes:
|
||||
* Aug 27, 2011 extracted from driver.c (A. Welzel)
|
||||
*
|
||||
* The entry points into this file are:
|
||||
* driver_task: the main message loop of the driver
|
||||
* driver_terminate: break out of the main message loop
|
||||
* driver_handle_msg: handle a single received message
|
||||
* driver_receive: message receive interface for drivers
|
||||
* driver_receive_mq: message receive interface; try the message queue first
|
||||
* driver_mq_queue: queue an incoming message for later processing
|
||||
*/
|
||||
|
||||
#include <minix/drivers.h>
|
||||
#include <minix/driver.h>
|
||||
|
||||
#include "driver.h"
|
||||
#include "mq.h"
|
||||
|
||||
PUBLIC endpoint_t device_endpt; /* used externally by log driver */
|
||||
PRIVATE int driver_running;
|
||||
|
||||
/*===========================================================================*
|
||||
* driver_receive *
|
||||
*===========================================================================*/
|
||||
PUBLIC int driver_receive(endpoint_t src, message *m_ptr, int *status_ptr)
|
||||
{
|
||||
/* receive() interface for drivers. */
|
||||
|
||||
return sef_receive_status(src, m_ptr, status_ptr);
|
||||
}
|
||||
|
||||
/*===========================================================================*
|
||||
* driver_receive_mq *
|
||||
*===========================================================================*/
|
||||
PUBLIC int driver_receive_mq(message *m_ptr, int *status_ptr)
|
||||
{
|
||||
/* receive() interface for drivers with message queueing. */
|
||||
|
||||
/* Any queued messages? */
|
||||
if (driver_mq_dequeue(DRIVER_MQ_SINGLE, m_ptr, status_ptr))
|
||||
return OK;
|
||||
|
||||
/* Fall back to standard receive() interface otherwise. */
|
||||
return driver_receive(ANY, m_ptr, status_ptr);
|
||||
}
|
||||
|
||||
/*===========================================================================*
|
||||
* driver_terminate *
|
||||
*===========================================================================*/
|
||||
PUBLIC void driver_terminate(void)
|
||||
{
|
||||
/* Break out of the main driver loop after finishing the current request. */
|
||||
|
||||
driver_running = FALSE;
|
||||
}
|
||||
|
||||
/*===========================================================================*
|
||||
* driver_task *
|
||||
*===========================================================================*/
|
||||
PUBLIC void driver_task(dp, type)
|
||||
struct driver *dp; /* Device dependent entry points. */
|
||||
int type; /* Driver type (DRIVER_STD or DRIVER_ASYN) */
|
||||
{
|
||||
/* Main program of any device driver task. */
|
||||
int r, ipc_status;
|
||||
message mess;
|
||||
|
||||
driver_running = TRUE;
|
||||
|
||||
/* Here is the main loop of the disk task. It waits for a message, carries
|
||||
* it out, and sends a reply.
|
||||
*/
|
||||
while (driver_running) {
|
||||
if ((r = driver_receive_mq(&mess, &ipc_status)) != OK)
|
||||
panic("driver_receive_mq failed: %d", r);
|
||||
|
||||
driver_handle_msg(dp, type, &mess, ipc_status);
|
||||
}
|
||||
}
|
||||
|
||||
/*===========================================================================*
|
||||
* driver_handle_msg *
|
||||
*===========================================================================*/
|
||||
PUBLIC void driver_handle_msg(struct driver *dp, int driver_type,
|
||||
message *m_ptr, int ipc_status)
|
||||
{
|
||||
/* Handle the given received message. */
|
||||
int r;
|
||||
|
||||
/* Dirty hack: set a global variable for the log driver. */
|
||||
device_endpt = m_ptr->USER_ENDPT;
|
||||
|
||||
/* Process the notification or request. */
|
||||
if (is_ipc_notify(ipc_status)) {
|
||||
driver_handle_notify(dp, m_ptr);
|
||||
|
||||
/* Do not reply to notifications. */
|
||||
} else {
|
||||
r = driver_handle_request(dp, m_ptr);
|
||||
|
||||
driver_reply(driver_type, m_ptr, ipc_status, r);
|
||||
}
|
||||
}
|
||||
|
||||
/*===========================================================================*
|
||||
* driver_mq_queue *
|
||||
*===========================================================================*/
|
||||
PUBLIC int driver_mq_queue(message *m, int status)
|
||||
{
|
||||
/* Queue a message for later processing. */
|
||||
|
||||
return driver_mq_enqueue(DRIVER_MQ_SINGLE, m, status);
|
||||
}
|
||||
@@ -1,14 +0,0 @@
|
||||
#ifndef _DRIVER_EVENT_H
|
||||
#define _DRIVER_EVENT_H
|
||||
|
||||
typedef struct {
|
||||
mthread_mutex_t mutex;
|
||||
mthread_cond_t cond;
|
||||
} event_t;
|
||||
|
||||
_PROTOTYPE( void driver_event_init, (event_t *event) );
|
||||
_PROTOTYPE( void driver_event_destroy, (event_t *event) );
|
||||
_PROTOTYPE( void driver_event_wait, (event_t *event) );
|
||||
_PROTOTYPE( void driver_event_fire, (event_t *event) );
|
||||
|
||||
#endif /* _DRIVER_EVENT_H */
|
||||
@@ -1,12 +0,0 @@
|
||||
#ifndef _DRIVER_MQ_H
|
||||
#define _DRIVER_MQ_H
|
||||
|
||||
#define DRIVER_MQ_SINGLE 0 /* thread ID for single-threading */
|
||||
|
||||
_PROTOTYPE( void driver_mq_init, (void) );
|
||||
_PROTOTYPE( int driver_mq_enqueue, (thread_id_t thread_id, const message *mess,
|
||||
int ipc_status) );
|
||||
_PROTOTYPE( int driver_mq_dequeue, (thread_id_t thread_id, message *mess,
|
||||
int *ipc_status) );
|
||||
|
||||
#endif /* _DRIVER_MQ_H */
|
||||
Reference in New Issue
Block a user