ed007ca416
This version of libbdev support asynchronous communication, recovery after driver restarts, and retrying of failed transfer operations.
122 lines
3.2 KiB
C
122 lines
3.2 KiB
C
/* libbdev - driver endpoint management */
|
|
|
|
#include <minix/drivers.h>
|
|
#include <minix/bdev.h>
|
|
#include <minix/ds.h>
|
|
#include <assert.h>
|
|
|
|
#include "const.h"
|
|
#include "type.h"
|
|
#include "proto.h"
|
|
|
|
static struct {
|
|
endpoint_t endpt;
|
|
char label[DS_MAX_KEYLEN];
|
|
} driver_tab[NR_DEVICES];
|
|
|
|
void bdev_driver_init(void)
|
|
{
|
|
/* Initialize the driver table.
|
|
*/
|
|
int i;
|
|
|
|
for (i = 0; i < NR_DEVICES; i++) {
|
|
driver_tab[i].endpt = NONE;
|
|
driver_tab[i].label[0] = '\0';
|
|
}
|
|
}
|
|
|
|
void bdev_driver_clear(dev_t dev)
|
|
{
|
|
/* Clear information about a driver.
|
|
*/
|
|
int major;
|
|
|
|
major = major(dev);
|
|
|
|
assert(major >= 0 && major < NR_DEVICES);
|
|
|
|
driver_tab[major].endpt = NONE;
|
|
driver_tab[major].label[0] = '\0';
|
|
}
|
|
|
|
endpoint_t bdev_driver_set(dev_t dev, char *label)
|
|
{
|
|
/* Set the label for a driver, and retrieve the associated endpoint.
|
|
*/
|
|
int major;
|
|
|
|
major = major(dev);
|
|
|
|
assert(major >= 0 && major < NR_DEVICES);
|
|
assert(strlen(label) < sizeof(driver_tab[major].label));
|
|
|
|
strcpy(driver_tab[major].label, label);
|
|
|
|
driver_tab[major].endpt = NONE;
|
|
|
|
return bdev_driver_update(dev);
|
|
}
|
|
|
|
endpoint_t bdev_driver_get(dev_t dev)
|
|
{
|
|
/* Return the endpoint for a driver, or NONE if we do not know its endpoint.
|
|
*/
|
|
int major;
|
|
|
|
major = major(dev);
|
|
|
|
assert(major >= 0 && major < NR_DEVICES);
|
|
|
|
return driver_tab[major].endpt;
|
|
}
|
|
|
|
endpoint_t bdev_driver_update(dev_t dev)
|
|
{
|
|
/* Update the endpoint of a driver. The caller of this function already knows
|
|
* that the current endpoint may no longer be valid, and must be updated.
|
|
* Return the new endpoint upon success, and NONE otherwise.
|
|
*/
|
|
endpoint_t endpt;
|
|
int r, major, nr_tries;
|
|
|
|
major = major(dev);
|
|
|
|
assert(major >= 0 && major < NR_DEVICES);
|
|
assert(driver_tab[major].label[0] != '\0');
|
|
|
|
/* Repeatedly retrieve the endpoint for the driver label, and see if it is a
|
|
* different, valid endpoint. If retrieval fails at first, we have to wait.
|
|
* We use polling, as opposed to a DS subscription, for a number of reasons:
|
|
* 1) DS supports only one subscription per process, and our main program may
|
|
* already have a subscription;
|
|
* 2) if we block on receiving a notification from DS, we cannot impose an
|
|
* upper bound on the retry time;
|
|
* 3) temporarily subscribing and then unsubscribing may cause leftover DS
|
|
* notifications, which the main program would then have to deal with.
|
|
* As of writing, unsubscribing from DS is not possible at all, anyway.
|
|
*
|
|
* In the normal case, the driver's label/endpoint mapping entry disappears
|
|
* completely for a short moment, before being replaced with the new mapping.
|
|
* Hence, failure to retrieve the entry at all does not constitute permanent
|
|
* failure. In fact, there is no way to determine reliably that a driver has
|
|
* failed permanently in the current approach. For this we simply rely on the
|
|
* retry limit.
|
|
*/
|
|
for (nr_tries = 0; nr_tries < DS_NR_TRIES; nr_tries++) {
|
|
r = ds_retrieve_label_endpt(driver_tab[major].label, &endpt);
|
|
|
|
if (r == OK && endpt != NONE && endpt != driver_tab[major].endpt) {
|
|
driver_tab[major].endpt = endpt;
|
|
|
|
return endpt;
|
|
}
|
|
|
|
if (nr_tries < DS_NR_TRIES - 1)
|
|
micro_delay(DS_DELAY);
|
|
}
|
|
|
|
driver_tab[major].endpt = NONE;
|
|
|
|
return NONE;
|
|
}
|