Changeset View
Changeset View
Standalone View
Standalone View
head/sys/kern/subr_bus.c
Show First 20 Lines • Show All 150 Lines • ▼ Show 20 Lines | |||||
static MALLOC_DEFINE(M_BUS, "bus", "Bus data structures"); | static MALLOC_DEFINE(M_BUS, "bus", "Bus data structures"); | ||||
static MALLOC_DEFINE(M_BUS_SC, "bus-sc", "Bus data structures, softc"); | static MALLOC_DEFINE(M_BUS_SC, "bus-sc", "Bus data structures, softc"); | ||||
EVENTHANDLER_LIST_DEFINE(device_attach); | EVENTHANDLER_LIST_DEFINE(device_attach); | ||||
EVENTHANDLER_LIST_DEFINE(device_detach); | EVENTHANDLER_LIST_DEFINE(device_detach); | ||||
EVENTHANDLER_LIST_DEFINE(dev_lookup); | EVENTHANDLER_LIST_DEFINE(dev_lookup); | ||||
static int bus_child_location_sb(device_t child, struct sbuf *sb); | |||||
static int bus_child_pnpinfo_sb(device_t child, struct sbuf *sb); | |||||
static void devctl2_init(void); | static void devctl2_init(void); | ||||
static bool device_frozen; | static bool device_frozen; | ||||
#define DRIVERNAME(d) ((d)? d->name : "no driver") | #define DRIVERNAME(d) ((d)? d->name : "no driver") | ||||
#define DEVCLANAME(d) ((d)? d->name : "no devclass") | #define DEVCLANAME(d) ((d)? d->name : "no devclass") | ||||
#ifdef BUS_DEBUG | #ifdef BUS_DEBUG | ||||
▲ Show 20 Lines • Show All 220 Lines • ▼ Show 20 Lines | static struct cdevsw dev_cdevsw = { | ||||
.d_close = devclose, | .d_close = devclose, | ||||
.d_read = devread, | .d_read = devread, | ||||
.d_ioctl = devioctl, | .d_ioctl = devioctl, | ||||
.d_poll = devpoll, | .d_poll = devpoll, | ||||
.d_kqfilter = devkqfilter, | .d_kqfilter = devkqfilter, | ||||
.d_name = "devctl", | .d_name = "devctl", | ||||
}; | }; | ||||
#define DEVCTL_BUFFER (1024 - sizeof(void *)) | |||||
struct dev_event_info { | struct dev_event_info { | ||||
char *dei_data; | |||||
STAILQ_ENTRY(dev_event_info) dei_link; | STAILQ_ENTRY(dev_event_info) dei_link; | ||||
char dei_data[DEVCTL_BUFFER]; | |||||
}; | }; | ||||
STAILQ_HEAD(devq, dev_event_info); | STAILQ_HEAD(devq, dev_event_info); | ||||
static struct dev_softc { | static struct dev_softc { | ||||
int inuse; | int inuse; | ||||
int nonblock; | int nonblock; | ||||
int queued; | int queued; | ||||
int async; | int async; | ||||
struct mtx mtx; | struct mtx mtx; | ||||
struct cv cv; | struct cv cv; | ||||
struct selinfo sel; | struct selinfo sel; | ||||
struct devq devq; | struct devq devq; | ||||
struct sigio *sigio; | struct sigio *sigio; | ||||
uma_zone_t zone; | |||||
} devsoftc; | } devsoftc; | ||||
static void filt_devctl_detach(struct knote *kn); | static void filt_devctl_detach(struct knote *kn); | ||||
static int filt_devctl_read(struct knote *kn, long hint); | static int filt_devctl_read(struct knote *kn, long hint); | ||||
struct filterops devctl_rfiltops = { | struct filterops devctl_rfiltops = { | ||||
.f_isfd = 1, | .f_isfd = 1, | ||||
.f_detach = filt_devctl_detach, | .f_detach = filt_devctl_detach, | ||||
.f_event = filt_devctl_read, | .f_event = filt_devctl_read, | ||||
}; | }; | ||||
static struct cdev *devctl_dev; | static struct cdev *devctl_dev; | ||||
static void | static void | ||||
devinit(void) | devinit(void) | ||||
{ | { | ||||
devctl_dev = make_dev_credf(MAKEDEV_ETERNAL, &dev_cdevsw, 0, NULL, | devctl_dev = make_dev_credf(MAKEDEV_ETERNAL, &dev_cdevsw, 0, NULL, | ||||
UID_ROOT, GID_WHEEL, 0600, "devctl"); | UID_ROOT, GID_WHEEL, 0600, "devctl"); | ||||
mtx_init(&devsoftc.mtx, "dev mtx", "devd", MTX_DEF); | mtx_init(&devsoftc.mtx, "dev mtx", "devd", MTX_DEF); | ||||
cv_init(&devsoftc.cv, "dev cv"); | cv_init(&devsoftc.cv, "dev cv"); | ||||
STAILQ_INIT(&devsoftc.devq); | STAILQ_INIT(&devsoftc.devq); | ||||
knlist_init_mtx(&devsoftc.sel.si_note, &devsoftc.mtx); | knlist_init_mtx(&devsoftc.sel.si_note, &devsoftc.mtx); | ||||
if (devctl_queue_length > 0) { | |||||
devsoftc.zone = uma_zcreate("DEVCTL", sizeof(struct dev_event_info), | |||||
NULL, NULL, NULL, NULL, UMA_ALIGN_PTR, UMA_ZONE_NOFREE); | |||||
uma_prealloc(devsoftc.zone, devctl_queue_length); | |||||
} | |||||
devctl2_init(); | devctl2_init(); | ||||
} | } | ||||
static int | static int | ||||
devopen(struct cdev *dev, int oflags, int devtype, struct thread *td) | devopen(struct cdev *dev, int oflags, int devtype, struct thread *td) | ||||
{ | { | ||||
mtx_lock(&devsoftc.mtx); | mtx_lock(&devsoftc.mtx); | ||||
if (devsoftc.inuse) { | if (devsoftc.inuse) { | ||||
▲ Show 20 Lines • Show All 48 Lines • ▼ Show 20 Lines | if (rv) { | ||||
return (rv); | return (rv); | ||||
} | } | ||||
} | } | ||||
n1 = STAILQ_FIRST(&devsoftc.devq); | n1 = STAILQ_FIRST(&devsoftc.devq); | ||||
STAILQ_REMOVE_HEAD(&devsoftc.devq, dei_link); | STAILQ_REMOVE_HEAD(&devsoftc.devq, dei_link); | ||||
devsoftc.queued--; | devsoftc.queued--; | ||||
mtx_unlock(&devsoftc.mtx); | mtx_unlock(&devsoftc.mtx); | ||||
rv = uiomove(n1->dei_data, strlen(n1->dei_data), uio); | rv = uiomove(n1->dei_data, strlen(n1->dei_data), uio); | ||||
free(n1->dei_data, M_BUS); | uma_zfree(devsoftc.zone, n1); | ||||
free(n1, M_BUS); | |||||
return (rv); | return (rv); | ||||
} | } | ||||
static int | static int | ||||
devioctl(struct cdev *dev, u_long cmd, caddr_t data, int fflag, struct thread *td) | devioctl(struct cdev *dev, u_long cmd, caddr_t data, int fflag, struct thread *td) | ||||
{ | { | ||||
switch (cmd) { | switch (cmd) { | ||||
case FIONBIO: | case FIONBIO: | ||||
▲ Show 20 Lines • Show All 72 Lines • ▼ Show 20 Lines | |||||
* @brief Return whether the userland process is running | * @brief Return whether the userland process is running | ||||
*/ | */ | ||||
boolean_t | boolean_t | ||||
devctl_process_running(void) | devctl_process_running(void) | ||||
{ | { | ||||
return (devsoftc.inuse == 1); | return (devsoftc.inuse == 1); | ||||
} | } | ||||
/** | static struct dev_event_info * | ||||
* @brief Queue data to be read from the devctl device | devctl_alloc_dei(void) | ||||
* | |||||
* Generic interface to queue data to the devctl device. It is | |||||
* assumed that @p data is properly formatted. It is further assumed | |||||
* that @p data is allocated using the M_BUS malloc type. | |||||
*/ | |||||
static void | |||||
devctl_queue_data_f(char *data, int flags) | |||||
{ | { | ||||
struct dev_event_info *n1 = NULL, *n2 = NULL; | struct dev_event_info *dei = NULL; | ||||
if (strlen(data) == 0) | mtx_lock(&devsoftc.mtx); | ||||
goto out; | |||||
if (devctl_queue_length == 0) | if (devctl_queue_length == 0) | ||||
goto out; | goto out; | ||||
n1 = malloc(sizeof(*n1), M_BUS, flags); | if (devctl_queue_length == devsoftc.queued) { | ||||
if (n1 == NULL) | dei = STAILQ_FIRST(&devsoftc.devq); | ||||
goto out; | |||||
n1->dei_data = data; | |||||
mtx_lock(&devsoftc.mtx); | |||||
if (devctl_queue_length == 0) { | |||||
mtx_unlock(&devsoftc.mtx); | |||||
free(n1->dei_data, M_BUS); | |||||
free(n1, M_BUS); | |||||
return; | |||||
} | |||||
/* Leave at least one spot in the queue... */ | |||||
while (devsoftc.queued > devctl_queue_length - 1) { | |||||
n2 = STAILQ_FIRST(&devsoftc.devq); | |||||
STAILQ_REMOVE_HEAD(&devsoftc.devq, dei_link); | STAILQ_REMOVE_HEAD(&devsoftc.devq, dei_link); | ||||
free(n2->dei_data, M_BUS); | |||||
free(n2, M_BUS); | |||||
devsoftc.queued--; | devsoftc.queued--; | ||||
} else { | |||||
/* dei can't be NULL -- we know we have at least one in the zone */ | |||||
dei = uma_zalloc(devsoftc.zone, M_NOWAIT); | |||||
MPASS(dei != NULL); | |||||
} | } | ||||
STAILQ_INSERT_TAIL(&devsoftc.devq, n1, dei_link); | *dei->dei_data = '\0'; | ||||
out: | |||||
mtx_unlock(&devsoftc.mtx); | |||||
return (dei); | |||||
} | |||||
static struct dev_event_info * | |||||
devctl_alloc_dei_sb(struct sbuf *sb) | |||||
{ | |||||
struct dev_event_info *dei; | |||||
dei = devctl_alloc_dei(); | |||||
if (dei != NULL) | |||||
sbuf_new(sb, dei->dei_data, sizeof(dei->dei_data), SBUF_FIXEDLEN); | |||||
return (dei); | |||||
} | |||||
static void | |||||
devctl_free_dei(struct dev_event_info *dei) | |||||
{ | |||||
uma_zfree(devsoftc.zone, dei); | |||||
} | |||||
static void | |||||
devctl_queue(struct dev_event_info *dei) | |||||
{ | |||||
mtx_lock(&devsoftc.mtx); | |||||
STAILQ_INSERT_TAIL(&devsoftc.devq, dei, dei_link); | |||||
devsoftc.queued++; | devsoftc.queued++; | ||||
cv_broadcast(&devsoftc.cv); | cv_broadcast(&devsoftc.cv); | ||||
KNOTE_LOCKED(&devsoftc.sel.si_note, 0); | KNOTE_LOCKED(&devsoftc.sel.si_note, 0); | ||||
mtx_unlock(&devsoftc.mtx); | mtx_unlock(&devsoftc.mtx); | ||||
selwakeup(&devsoftc.sel); | selwakeup(&devsoftc.sel); | ||||
if (devsoftc.async && devsoftc.sigio != NULL) | if (devsoftc.async && devsoftc.sigio != NULL) | ||||
pgsigio(&devsoftc.sigio, SIGIO, 0); | pgsigio(&devsoftc.sigio, SIGIO, 0); | ||||
return; | |||||
out: | |||||
/* | |||||
* We have to free data on all error paths since the caller | |||||
* assumes it will be free'd when this item is dequeued. | |||||
*/ | |||||
free(data, M_BUS); | |||||
return; | |||||
} | } | ||||
static void | |||||
devctl_queue_data(char *data) | |||||
{ | |||||
devctl_queue_data_f(data, M_NOWAIT); | |||||
} | |||||
/** | /** | ||||
* @brief Send a 'notification' to userland, using standard ways | * @brief Send a 'notification' to userland, using standard ways | ||||
*/ | */ | ||||
void | void | ||||
devctl_notify_f(const char *system, const char *subsystem, const char *type, | devctl_notify_f(const char *system, const char *subsystem, const char *type, | ||||
const char *data, int flags) | const char *data, int flags __unused) | ||||
{ | { | ||||
int len = 0; | struct dev_event_info *dei; | ||||
char *msg; | struct sbuf sb; | ||||
if (system == NULL) | if (system == NULL || subsystem == NULL || type == NULL) | ||||
return; /* BOGUS! Must specify system. */ | return; | ||||
if (subsystem == NULL) | dei = devctl_alloc_dei_sb(&sb); | ||||
return; /* BOGUS! Must specify subsystem. */ | if (dei == NULL) | ||||
if (type == NULL) | return; | ||||
return; /* BOGUS! Must specify type. */ | sbuf_cpy(&sb, "!system="); | ||||
len += strlen(" system=") + strlen(system); | sbuf_cat(&sb, system); | ||||
len += strlen(" subsystem=") + strlen(subsystem); | sbuf_cat(&sb, " subsystem="); | ||||
len += strlen(" type=") + strlen(type); | sbuf_cat(&sb, subsystem); | ||||
/* add in the data message plus newline. */ | sbuf_cat(&sb, " type="); | ||||
if (data != NULL) | sbuf_cat(&sb, type); | ||||
len += strlen(data); | if (data != NULL) { | ||||
len += 3; /* '!', '\n', and NUL */ | sbuf_putc(&sb, ' '); | ||||
msg = malloc(len, M_BUS, flags); | sbuf_cat(&sb, data); | ||||
if (msg == NULL) | } | ||||
return; /* Drop it on the floor */ | sbuf_putc(&sb, '\n'); | ||||
if (data != NULL) | if (sbuf_finish(&sb) != 0) | ||||
snprintf(msg, len, "!system=%s subsystem=%s type=%s %s\n", | devctl_free_dei(dei); /* overflow -> drop it */ | ||||
system, subsystem, type, data); | |||||
else | else | ||||
snprintf(msg, len, "!system=%s subsystem=%s type=%s\n", | devctl_queue(dei); | ||||
system, subsystem, type); | |||||
devctl_queue_data_f(msg, flags); | |||||
} | } | ||||
void | void | ||||
devctl_notify(const char *system, const char *subsystem, const char *type, | devctl_notify(const char *system, const char *subsystem, const char *type, | ||||
const char *data) | const char *data) | ||||
{ | { | ||||
devctl_notify_f(system, subsystem, type, data, M_NOWAIT); | devctl_notify_f(system, subsystem, type, data, M_NOWAIT); | ||||
} | } | ||||
/* | /* | ||||
* Common routine that tries to make sending messages as easy as possible. | * Common routine that tries to make sending messages as easy as possible. | ||||
* We allocate memory for the data, copy strings into that, but do not | * We allocate memory for the data, copy strings into that, but do not | ||||
* free it unless there's an error. The dequeue part of the driver should | * free it unless there's an error. The dequeue part of the driver should | ||||
* free the data. We don't send data when the device is disabled. We do | * free the data. We don't send data when the device is disabled. We do | ||||
* send data, even when we have no listeners, because we wish to avoid | * send data, even when we have no listeners, because we wish to avoid | ||||
* races relating to startup and restart of listening applications. | * races relating to startup and restart of listening applications. | ||||
* | * | ||||
* devaddq is designed to string together the type of event, with the | * devaddq is designed to string together the type of event, with the | ||||
* object of that event, plus the plug and play info and location info | * object of that event, plus the plug and play info and location info | ||||
* for that event. This is likely most useful for devices, but less | * for that event. This is likely most useful for devices, but less | ||||
* useful for other consumers of this interface. Those should use | * useful for other consumers of this interface. Those should use | ||||
* the devctl_queue_data() interface instead. | * the devctl_notify() interface instead. | ||||
* | |||||
* Output: | |||||
* ${type}${what} at $(location dev) $(pnp-info dev) on $(parent dev) | |||||
*/ | */ | ||||
static void | static void | ||||
devaddq(const char *type, const char *what, device_t dev) | devaddq(const char *type, const char *what, device_t dev) | ||||
{ | { | ||||
char *data = NULL; | struct dev_event_info *dei; | ||||
char *loc = NULL; | |||||
char *pnp = NULL; | |||||
const char *parstr; | const char *parstr; | ||||
struct sbuf sb; | |||||
if (!devctl_queue_length)/* Rare race, but lost races safely discard */ | dei = devctl_alloc_dei_sb(&sb); | ||||
if (dei == NULL) | |||||
return; | return; | ||||
data = malloc(1024, M_BUS, M_NOWAIT); | sbuf_cpy(&sb, type); | ||||
if (data == NULL) | sbuf_cat(&sb, what); | ||||
goto bad; | sbuf_cat(&sb, " at "); | ||||
/* get the bus specific location of this device */ | /* Add in the location */ | ||||
loc = malloc(1024, M_BUS, M_NOWAIT); | bus_child_location_sb(dev, &sb); | ||||
if (loc == NULL) | sbuf_putc(&sb, ' '); | ||||
goto bad; | |||||
*loc = '\0'; | |||||
bus_child_location_str(dev, loc, 1024); | |||||
/* Get the bus specific pnp info of this device */ | /* Add in pnpinfo */ | ||||
pnp = malloc(1024, M_BUS, M_NOWAIT); | bus_child_pnpinfo_sb(dev, &sb); | ||||
if (pnp == NULL) | |||||
goto bad; | |||||
*pnp = '\0'; | |||||
bus_child_pnpinfo_str(dev, pnp, 1024); | |||||
/* Get the parent of this device, or / if high enough in the tree. */ | /* Get the parent of this device, or / if high enough in the tree. */ | ||||
if (device_get_parent(dev) == NULL) | if (device_get_parent(dev) == NULL) | ||||
parstr = "."; /* Or '/' ? */ | parstr = "."; /* Or '/' ? */ | ||||
else | else | ||||
parstr = device_get_nameunit(device_get_parent(dev)); | parstr = device_get_nameunit(device_get_parent(dev)); | ||||
/* String it all together. */ | sbuf_cat(&sb, " on "); | ||||
snprintf(data, 1024, "%s%s at %s %s on %s\n", type, what, loc, pnp, | sbuf_cat(&sb, parstr); | ||||
parstr); | sbuf_putc(&sb, '\n'); | ||||
free(loc, M_BUS); | if (sbuf_finish(&sb) != 0) | ||||
free(pnp, M_BUS); | goto bad; | ||||
devctl_queue_data(data); | devctl_queue(dei); | ||||
return; | return; | ||||
bad: | bad: | ||||
free(pnp, M_BUS); | devctl_free_dei(dei); | ||||
free(loc, M_BUS); | |||||
free(data, M_BUS); | |||||
return; | |||||
} | } | ||||
/* | /* | ||||
* A device was added to the tree. We are called just after it successfully | * A device was added to the tree. We are called just after it successfully | ||||
* attaches (that is, probe and attach success for this device). No call | * attaches (that is, probe and attach success for this device). No call | ||||
* is made if a device is merely parented into the tree. See devnomatch | * is made if a device is merely parented into the tree. See devnomatch | ||||
* if probe fails. If attach fails, no notification is sent (but maybe | * if probe fails. If attach fails, no notification is sent (but maybe | ||||
* we should have a different message for this). | * we should have a different message for this). | ||||
Show All 25 Lines | |||||
devnomatch(device_t dev) | devnomatch(device_t dev) | ||||
{ | { | ||||
devaddq("?", "", dev); | devaddq("?", "", dev); | ||||
} | } | ||||
static int | static int | ||||
sysctl_devctl_queue(SYSCTL_HANDLER_ARGS) | sysctl_devctl_queue(SYSCTL_HANDLER_ARGS) | ||||
{ | { | ||||
struct dev_event_info *n1; | |||||
int q, error; | int q, error; | ||||
q = devctl_queue_length; | q = devctl_queue_length; | ||||
error = sysctl_handle_int(oidp, &q, 0, req); | error = sysctl_handle_int(oidp, &q, 0, req); | ||||
if (error || !req->newptr) | if (error || !req->newptr) | ||||
return (error); | return (error); | ||||
if (q < 0) | if (q < 0) | ||||
return (EINVAL); | return (EINVAL); | ||||
if (mtx_initialized(&devsoftc.mtx)) | |||||
mtx_lock(&devsoftc.mtx); | /* | ||||
* When set as a tunable, we've not yet initialized the mutex. | |||||
* It is safe to just assign to devctl_queue_length and return | |||||
* as we're racing no one. We'll use whatever value set in | |||||
* devinit. | |||||
*/ | |||||
if (!mtx_initialized(&devsoftc.mtx)) { | |||||
devctl_queue_length = q; | devctl_queue_length = q; | ||||
while (devsoftc.queued > devctl_queue_length) { | return (0); | ||||
n1 = STAILQ_FIRST(&devsoftc.devq); | |||||
STAILQ_REMOVE_HEAD(&devsoftc.devq, dei_link); | |||||
free(n1->dei_data, M_BUS); | |||||
free(n1, M_BUS); | |||||
devsoftc.queued--; | |||||
} | } | ||||
if (mtx_initialized(&devsoftc.mtx)) | |||||
/* | |||||
* XXX It's hard to grow or shrink the UMA zone. Only allow | |||||
* disabling the queue size for the moment until underlying | |||||
* UMA issues can be sorted out. | |||||
*/ | |||||
if (q != 0) | |||||
return (EINVAL); | |||||
if (q == devctl_queue_length) | |||||
return (0); | |||||
mtx_lock(&devsoftc.mtx); | |||||
devctl_queue_length = 0; | |||||
uma_zdestroy(devsoftc.zone); | |||||
devsoftc.zone = 0; | |||||
mtx_unlock(&devsoftc.mtx); | mtx_unlock(&devsoftc.mtx); | ||||
return (0); | return (0); | ||||
} | } | ||||
/** | /** | ||||
* @brief safely quotes strings that might have double quotes in them. | * @brief safely quotes strings that might have double quotes in them. | ||||
* | * | ||||
* The devctl protocol relies on quoted strings having matching quotes. | * The devctl protocol relies on quoted strings having matching quotes. | ||||
* This routine quotes any internal quotes so the resulting string | * This routine quotes any internal quotes so the resulting string | ||||
▲ Show 20 Lines • Show All 4,095 Lines • ▼ Show 20 Lines | bus_child_location_str(device_t child, char *buf, size_t buflen) | ||||
parent = device_get_parent(child); | parent = device_get_parent(child); | ||||
if (parent == NULL) { | if (parent == NULL) { | ||||
*buf = '\0'; | *buf = '\0'; | ||||
return (0); | return (0); | ||||
} | } | ||||
return (BUS_CHILD_LOCATION_STR(parent, child, buf, buflen)); | return (BUS_CHILD_LOCATION_STR(parent, child, buf, buflen)); | ||||
} | } | ||||
/** | |||||
* @brief Wrapper function for bus_child_pnpinfo_str using sbuf | |||||
* | |||||
* A convenient wrapper frunction for bus_child_pnpinfo_str that allows | |||||
* us to splat that into an sbuf. It uses unholy knowledge of sbuf to | |||||
* accomplish this, however. It is an interim function until we can convert | |||||
* this interface more fully. | |||||
*/ | |||||
/* Note: we reach inside of sbuf because it's API isn't rich enough to do this */ | |||||
#define SPACE(s) ((s)->s_size - (s)->s_len) | |||||
#define EOB(s) ((s)->s_buf + (s)->s_len) | |||||
static int | |||||
bus_child_pnpinfo_sb(device_t dev, struct sbuf *sb) | |||||
{ | |||||
char *p; | |||||
size_t space; | |||||
MPASS((sb->s_flags & SBUF_INCLUDENUL) == 0); | |||||
if (sb->s_error != 0) | |||||
return (-1); | |||||
p = EOB(sb); | |||||
*p = '\0'; /* sbuf buffer isn't NUL terminated until sbuf_finish() */ | |||||
space = SPACE(sb); | |||||
if (space <= 1) { | |||||
sb->s_error = ENOMEM; | |||||
return (-1); | |||||
} | |||||
bus_child_pnpinfo_str(dev, p, space); | |||||
sb->s_len += strlen(p); | |||||
return (0); | |||||
} | |||||
/** | |||||
* @brief Wrapper function for bus_child_pnpinfo_str using sbuf | |||||
* | |||||
* A convenient wrapper frunction for bus_child_pnpinfo_str that allows | |||||
* us to splat that into an sbuf. It uses unholy knowledge of sbuf to | |||||
* accomplish this, however. It is an interim function until we can convert | |||||
* this interface more fully. | |||||
*/ | |||||
static int | |||||
bus_child_location_sb(device_t dev, struct sbuf *sb) | |||||
{ | |||||
char *p; | |||||
size_t space; | |||||
MPASS((sb->s_flags & SBUF_INCLUDENUL) == 0); | |||||
if (sb->s_error != 0) | |||||
return (-1); | |||||
p = EOB(sb); | |||||
*p = '\0'; /* sbuf buffer isn't NUL terminated until sbuf_finish() */ | |||||
space = SPACE(sb); | |||||
if (space <= 1) { | |||||
sb->s_error = ENOMEM; | |||||
return (-1); | |||||
} | |||||
bus_child_location_str(dev, p, space); | |||||
sb->s_len += strlen(p); | |||||
return (0); | |||||
} | |||||
#undef SPACE | |||||
#undef EOB | |||||
/** | /** | ||||
* @brief Wrapper function for BUS_GET_CPUS(). | * @brief Wrapper function for BUS_GET_CPUS(). | ||||
* | * | ||||
* This function simply calls the BUS_GET_CPUS() method of the | * This function simply calls the BUS_GET_CPUS() method of the | ||||
* parent of @p dev. | * parent of @p dev. | ||||
*/ | */ | ||||
int | int | ||||
▲ Show 20 Lines • Show All 1,028 Lines • Show Last 20 Lines |