2008-01-08 09:27:46 +03:00
|
|
|
/* $NetBSD: subr_autoconf.c,v 1.128 2008/01/08 06:27:46 dyoung Exp $ */
|
2000-06-04 23:14:14 +04:00
|
|
|
|
|
|
|
/*
|
|
|
|
* Copyright (c) 1996, 2000 Christopher G. Demetriou
|
|
|
|
* All rights reserved.
|
2005-02-27 00:34:55 +03:00
|
|
|
*
|
2000-06-04 23:14:14 +04:00
|
|
|
* Redistribution and use in source and binary forms, with or without
|
|
|
|
* modification, are permitted provided that the following conditions
|
|
|
|
* are met:
|
|
|
|
* 1. Redistributions of source code must retain the above copyright
|
|
|
|
* notice, this list of conditions and the following disclaimer.
|
|
|
|
* 2. Redistributions in binary form must reproduce the above copyright
|
|
|
|
* notice, this list of conditions and the following disclaimer in the
|
|
|
|
* documentation and/or other materials provided with the distribution.
|
|
|
|
* 3. All advertising materials mentioning features or use of this software
|
|
|
|
* must display the following acknowledgement:
|
2000-06-14 02:36:16 +04:00
|
|
|
* This product includes software developed for the
|
2003-11-17 13:07:58 +03:00
|
|
|
* NetBSD Project. See http://www.NetBSD.org/ for
|
2000-06-14 02:36:16 +04:00
|
|
|
* information about NetBSD.
|
2000-06-04 23:14:14 +04:00
|
|
|
* 4. The name of the author may not be used to endorse or promote products
|
2000-06-14 02:36:16 +04:00
|
|
|
* derived from this software without specific prior written permission.
|
2005-02-27 00:34:55 +03:00
|
|
|
*
|
2000-06-04 23:14:14 +04:00
|
|
|
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
|
|
|
|
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
|
|
|
|
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
|
|
|
|
* IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
|
|
|
|
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
|
|
|
|
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
|
|
|
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
|
|
|
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
|
|
|
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
|
|
|
|
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
2005-02-27 00:34:55 +03:00
|
|
|
*
|
2000-06-14 02:36:16 +04:00
|
|
|
* --(license Id: LICENSE.proto,v 1.1 2000/06/13 21:40:26 cgd Exp )--
|
2000-06-04 23:14:14 +04:00
|
|
|
*/
|
1994-06-29 10:29:24 +04:00
|
|
|
|
1993-08-13 17:19:41 +04:00
|
|
|
/*
|
1994-05-20 08:15:04 +04:00
|
|
|
* Copyright (c) 1992, 1993
|
|
|
|
* The Regents of the University of California. All rights reserved.
|
1993-08-13 17:19:41 +04:00
|
|
|
*
|
|
|
|
* This software was developed by the Computer Systems Engineering group
|
|
|
|
* at Lawrence Berkeley Laboratory under DARPA contract BG 91-66 and
|
|
|
|
* contributed to Berkeley.
|
|
|
|
*
|
|
|
|
* All advertising materials mentioning features or use of this software
|
|
|
|
* must display the following acknowledgement:
|
|
|
|
* This product includes software developed by the University of
|
|
|
|
* California, Lawrence Berkeley Laboratories.
|
|
|
|
*
|
1994-05-20 08:15:04 +04:00
|
|
|
* Redistribution and use in source and binary forms, with or without
|
|
|
|
* modification, are permitted provided that the following conditions
|
|
|
|
* are met:
|
|
|
|
* 1. Redistributions of source code must retain the above copyright
|
|
|
|
* notice, this list of conditions and the following disclaimer.
|
|
|
|
* 2. Redistributions in binary form must reproduce the above copyright
|
|
|
|
* notice, this list of conditions and the following disclaimer in the
|
|
|
|
* documentation and/or other materials provided with the distribution.
|
2003-08-07 20:26:28 +04:00
|
|
|
* 3. Neither the name of the University nor the names of its contributors
|
1994-05-20 08:15:04 +04:00
|
|
|
* may be used to endorse or promote products derived from this software
|
|
|
|
* without specific prior written permission.
|
1993-08-13 17:19:41 +04:00
|
|
|
*
|
1994-05-20 08:15:04 +04:00
|
|
|
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
|
|
|
|
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
|
|
|
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
|
|
|
* ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
|
|
|
|
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
|
|
|
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
|
|
|
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
|
|
|
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
|
|
|
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
|
|
|
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
|
|
|
* SUCH DAMAGE.
|
1993-08-13 17:19:41 +04:00
|
|
|
*
|
1994-05-20 08:15:04 +04:00
|
|
|
* from: Header: subr_autoconf.c,v 1.12 93/02/01 19:31:48 torek Exp (LBL)
|
1994-06-29 10:29:24 +04:00
|
|
|
*
|
1998-03-01 05:20:01 +03:00
|
|
|
* @(#)subr_autoconf.c 8.3 (Berkeley) 5/17/94
|
1993-08-13 17:19:41 +04:00
|
|
|
*/
|
|
|
|
|
2000-06-02 05:31:52 +04:00
|
|
|
#include <sys/cdefs.h>
|
2008-01-08 09:27:46 +03:00
|
|
|
__KERNEL_RCSID(0, "$NetBSD: subr_autoconf.c,v 1.128 2008/01/08 06:27:46 dyoung Exp $");
|
2002-02-15 14:18:26 +03:00
|
|
|
|
2007-11-14 22:45:44 +03:00
|
|
|
#include "opt_multiprocessor.h"
|
2002-02-15 14:18:26 +03:00
|
|
|
#include "opt_ddb.h"
|
2000-06-02 05:31:52 +04:00
|
|
|
|
1993-12-18 06:59:02 +03:00
|
|
|
#include <sys/param.h>
|
|
|
|
#include <sys/device.h>
|
Extract common code from i386, xen, and sparc64, creating
config_handle_wedges() and read_disk_sectors(). On x86, handle_wedges()
is a thin wrapper for config_handle_wedges(). Share opendisk()
across architectures.
Add kernel code in support of specifying a root partition by wedge
name. E.g., root specifications "wedge:wd0a", "wedge:David's Root
Volume" are possible. (Patches for config(1) coming soon.)
In support of moving disks between architectures (esp. i386 <->
evbmips), I've written a routine convertdisklabel() that ensures
that the raw partition is at RAW_DISK by following these steps:
0 If we have read a disklabel that has a RAW_PART with
p_offset == 0 and p_size != 0, then use that raw partition.
1 If we have read a disklabel that has both partitions 'c'
and 'd', and RAW_PART has p_offset != 0 or p_size == 0,
but the other partition is suitable for a raw partition
(p_offset == 0, p_size != 0), then swap the two partitions
and use the new raw partition.
2 If the architecture's raw partition is 'd', and if there
is no partition 'd', but there is a partition 'c' that
is suitable for a raw partition, then copy partition 'c'
to partition 'd'.
3 Determine the drive's last sector, using either the
d_secperunit the drive reported, or by guessing (0x1fffffff).
If we cannot read the drive's last sector, then fail.
4 If we have read a disklabel that has no partition slot
RAW_PART, then create a partition RAW_PART. Make it span
the whole drive.
5 If there are fewer than MAXPARTITIONS partitions,
then "slide" the unsuitable raw partition RAW_PART, and
subsequent partitions, into partition slots RAW_PART+1
and subsequent slots. Create a raw partition at RAW_PART.
Make it span the whole drive.
The convertdisklabel() procedure can probably stand to be simplified,
but it ought to deal with all but an extraordinarily broken disklabel,
now.
i386: compiled and tested, sparc64: compiled, evbmips: compiled.
2007-06-24 05:43:34 +04:00
|
|
|
#include <sys/disklabel.h>
|
|
|
|
#include <sys/conf.h>
|
|
|
|
#include <sys/kauth.h>
|
1993-12-18 06:59:02 +03:00
|
|
|
#include <sys/malloc.h>
|
1996-02-04 05:15:01 +03:00
|
|
|
#include <sys/systm.h>
|
1999-09-18 00:11:56 +04:00
|
|
|
#include <sys/kernel.h>
|
1998-11-17 11:38:07 +03:00
|
|
|
#include <sys/errno.h>
|
2000-01-24 21:03:19 +03:00
|
|
|
#include <sys/proc.h>
|
2003-01-02 03:12:16 +03:00
|
|
|
#include <sys/reboot.h>
|
Extract common code from i386, xen, and sparc64, creating
config_handle_wedges() and read_disk_sectors(). On x86, handle_wedges()
is a thin wrapper for config_handle_wedges(). Share opendisk()
across architectures.
Add kernel code in support of specifying a root partition by wedge
name. E.g., root specifications "wedge:wd0a", "wedge:David's Root
Volume" are possible. (Patches for config(1) coming soon.)
In support of moving disks between architectures (esp. i386 <->
evbmips), I've written a routine convertdisklabel() that ensures
that the raw partition is at RAW_DISK by following these steps:
0 If we have read a disklabel that has a RAW_PART with
p_offset == 0 and p_size != 0, then use that raw partition.
1 If we have read a disklabel that has both partitions 'c'
and 'd', and RAW_PART has p_offset != 0 or p_size == 0,
but the other partition is suitable for a raw partition
(p_offset == 0, p_size != 0), then swap the two partitions
and use the new raw partition.
2 If the architecture's raw partition is 'd', and if there
is no partition 'd', but there is a partition 'c' that
is suitable for a raw partition, then copy partition 'c'
to partition 'd'.
3 Determine the drive's last sector, using either the
d_secperunit the drive reported, or by guessing (0x1fffffff).
If we cannot read the drive's last sector, then fail.
4 If we have read a disklabel that has no partition slot
RAW_PART, then create a partition RAW_PART. Make it span
the whole drive.
5 If there are fewer than MAXPARTITIONS partitions,
then "slide" the unsuitable raw partition RAW_PART, and
subsequent partitions, into partition slots RAW_PART+1
and subsequent slots. Create a raw partition at RAW_PART.
Make it span the whole drive.
The convertdisklabel() procedure can probably stand to be simplified,
but it ought to deal with all but an extraordinarily broken disklabel,
now.
i386: compiled and tested, sparc64: compiled, evbmips: compiled.
2007-06-24 05:43:34 +04:00
|
|
|
|
|
|
|
#include <sys/buf.h>
|
|
|
|
#include <sys/dirent.h>
|
|
|
|
#include <sys/vnode.h>
|
|
|
|
#include <sys/mount.h>
|
|
|
|
#include <sys/namei.h>
|
|
|
|
#include <sys/unistd.h>
|
|
|
|
#include <sys/fcntl.h>
|
|
|
|
#include <sys/lockf.h>
|
2007-12-09 23:27:42 +03:00
|
|
|
#include <sys/callout.h>
|
Extract common code from i386, xen, and sparc64, creating
config_handle_wedges() and read_disk_sectors(). On x86, handle_wedges()
is a thin wrapper for config_handle_wedges(). Share opendisk()
across architectures.
Add kernel code in support of specifying a root partition by wedge
name. E.g., root specifications "wedge:wd0a", "wedge:David's Root
Volume" are possible. (Patches for config(1) coming soon.)
In support of moving disks between architectures (esp. i386 <->
evbmips), I've written a routine convertdisklabel() that ensures
that the raw partition is at RAW_DISK by following these steps:
0 If we have read a disklabel that has a RAW_PART with
p_offset == 0 and p_size != 0, then use that raw partition.
1 If we have read a disklabel that has both partitions 'c'
and 'd', and RAW_PART has p_offset != 0 or p_size == 0,
but the other partition is suitable for a raw partition
(p_offset == 0, p_size != 0), then swap the two partitions
and use the new raw partition.
2 If the architecture's raw partition is 'd', and if there
is no partition 'd', but there is a partition 'c' that
is suitable for a raw partition, then copy partition 'c'
to partition 'd'.
3 Determine the drive's last sector, using either the
d_secperunit the drive reported, or by guessing (0x1fffffff).
If we cannot read the drive's last sector, then fail.
4 If we have read a disklabel that has no partition slot
RAW_PART, then create a partition RAW_PART. Make it span
the whole drive.
5 If there are fewer than MAXPARTITIONS partitions,
then "slide" the unsuitable raw partition RAW_PART, and
subsequent partitions, into partition slots RAW_PART+1
and subsequent slots. Create a raw partition at RAW_PART.
Make it span the whole drive.
The convertdisklabel() procedure can probably stand to be simplified,
but it ought to deal with all but an extraordinarily broken disklabel,
now.
i386: compiled and tested, sparc64: compiled, evbmips: compiled.
2007-06-24 05:43:34 +04:00
|
|
|
|
|
|
|
#include <sys/disk.h>
|
|
|
|
|
1994-11-04 09:40:11 +03:00
|
|
|
#include <machine/limits.h>
|
1993-08-13 17:19:41 +04:00
|
|
|
|
2001-07-01 06:56:20 +04:00
|
|
|
#include "opt_userconf.h"
|
|
|
|
#ifdef USERCONF
|
|
|
|
#include <sys/userconf.h>
|
|
|
|
#endif
|
|
|
|
|
2006-02-18 22:40:42 +03:00
|
|
|
#ifdef __i386__
|
2006-02-18 22:09:53 +03:00
|
|
|
#include "opt_splash.h"
|
|
|
|
#if defined(SPLASHSCREEN) && defined(SPLASHSCREEN_PROGRESS)
|
|
|
|
#include <dev/splash/splash.h>
|
|
|
|
extern struct splash_progress *splash_progress_state;
|
|
|
|
#endif
|
2006-02-18 22:40:42 +03:00
|
|
|
#endif
|
2006-02-18 22:09:53 +03:00
|
|
|
|
1993-08-13 17:19:41 +04:00
|
|
|
/*
|
|
|
|
* Autoconfiguration subroutines.
|
|
|
|
*/
|
|
|
|
|
|
|
|
/*
|
|
|
|
* ioconf.c exports exactly two names: cfdata and cfroots. All system
|
|
|
|
* devices and drivers are found via these tables.
|
|
|
|
*/
|
|
|
|
extern struct cfdata cfdata[];
|
2003-03-16 11:09:58 +03:00
|
|
|
extern const short cfroots[];
|
1993-08-13 17:19:41 +04:00
|
|
|
|
2002-09-27 06:24:06 +04:00
|
|
|
/*
|
|
|
|
* List of all cfdriver structures. We use this to detect duplicates
|
|
|
|
* when other cfdrivers are loaded.
|
|
|
|
*/
|
2002-09-27 09:45:03 +04:00
|
|
|
struct cfdriverlist allcfdrivers = LIST_HEAD_INITIALIZER(&allcfdrivers);
|
|
|
|
extern struct cfdriver * const cfdriver_list_initial[];
|
2002-09-27 06:24:06 +04:00
|
|
|
|
2002-10-04 05:50:53 +04:00
|
|
|
/*
|
|
|
|
* Initial list of cfattach's.
|
|
|
|
*/
|
|
|
|
extern const struct cfattachinit cfattachinit[];
|
|
|
|
|
2002-09-24 03:16:06 +04:00
|
|
|
/*
|
|
|
|
* List of cfdata tables. We always have one such list -- the one
|
|
|
|
* built statically when the kernel was configured.
|
|
|
|
*/
|
2007-11-12 02:22:23 +03:00
|
|
|
struct cftablelist allcftables = TAILQ_HEAD_INITIALIZER(allcftables);
|
2002-09-24 03:16:06 +04:00
|
|
|
static struct cftable initcftable;
|
|
|
|
|
2005-12-20 07:39:36 +03:00
|
|
|
#define ROOT ((device_t)NULL)
|
1993-08-13 17:19:41 +04:00
|
|
|
|
1994-11-04 09:40:11 +03:00
|
|
|
struct matchinfo {
|
2005-08-26 18:20:40 +04:00
|
|
|
cfsubmatch_t fn;
|
1994-11-04 09:40:11 +03:00
|
|
|
struct device *parent;
|
2005-08-26 18:20:40 +04:00
|
|
|
const int *locs;
|
1996-12-05 03:09:10 +03:00
|
|
|
void *aux;
|
|
|
|
struct cfdata *match;
|
|
|
|
int pri;
|
1994-11-04 09:40:11 +03:00
|
|
|
};
|
|
|
|
|
2000-06-02 05:31:52 +04:00
|
|
|
static char *number(char *, int);
|
2005-12-20 07:39:36 +03:00
|
|
|
static void mapply(struct matchinfo *, cfdata_t);
|
2007-03-05 23:32:43 +03:00
|
|
|
static device_t config_devalloc(const device_t, const cfdata_t, const int *);
|
|
|
|
static void config_devdealloc(device_t);
|
|
|
|
static void config_makeroom(int, struct cfdriver *);
|
|
|
|
static void config_devlink(device_t);
|
|
|
|
static void config_devunlink(device_t);
|
1996-02-04 05:15:01 +03:00
|
|
|
|
1998-06-09 22:46:12 +04:00
|
|
|
struct deferred_config {
|
|
|
|
TAILQ_ENTRY(deferred_config) dc_queue;
|
2005-12-20 07:39:36 +03:00
|
|
|
device_t dc_dev;
|
|
|
|
void (*dc_func)(device_t);
|
1998-06-09 22:46:12 +04:00
|
|
|
};
|
|
|
|
|
1999-09-15 23:37:08 +04:00
|
|
|
TAILQ_HEAD(deferred_config_head, deferred_config);
|
1998-06-09 22:46:12 +04:00
|
|
|
|
2007-11-12 02:22:23 +03:00
|
|
|
struct deferred_config_head deferred_config_queue =
|
|
|
|
TAILQ_HEAD_INITIALIZER(deferred_config_queue);
|
|
|
|
struct deferred_config_head interrupt_config_queue =
|
|
|
|
TAILQ_HEAD_INITIALIZER(interrupt_config_queue);
|
1999-09-15 23:37:08 +04:00
|
|
|
|
2005-12-20 07:39:36 +03:00
|
|
|
static void config_process_deferred(struct deferred_config_head *, device_t);
|
1998-06-09 22:46:12 +04:00
|
|
|
|
2002-10-01 22:11:57 +04:00
|
|
|
/* Hooks to finalize configuration once all real devices have been found. */
|
|
|
|
struct finalize_hook {
|
|
|
|
TAILQ_ENTRY(finalize_hook) f_list;
|
2005-12-20 07:39:36 +03:00
|
|
|
int (*f_func)(device_t);
|
|
|
|
device_t f_dev;
|
2002-10-01 22:11:57 +04:00
|
|
|
};
|
2007-11-12 02:22:23 +03:00
|
|
|
static TAILQ_HEAD(, finalize_hook) config_finalize_list =
|
|
|
|
TAILQ_HEAD_INITIALIZER(config_finalize_list);
|
2002-10-01 22:11:57 +04:00
|
|
|
static int config_finalize_done;
|
|
|
|
|
2001-05-28 20:40:31 +04:00
|
|
|
/* list of all devices */
|
2007-11-12 02:22:23 +03:00
|
|
|
struct devicelist alldevs = TAILQ_HEAD_INITIALIZER(alldevs);
|
2001-05-28 20:40:31 +04:00
|
|
|
|
2005-12-24 22:12:23 +03:00
|
|
|
volatile int config_pending; /* semaphore for mountroot */
|
2000-01-24 21:03:19 +03:00
|
|
|
|
2002-09-27 06:24:06 +04:00
|
|
|
#define STREQ(s1, s2) \
|
2002-09-27 10:12:55 +04:00
|
|
|
(*(s1) == *(s2) && strcmp((s1), (s2)) == 0)
|
2002-09-27 06:24:06 +04:00
|
|
|
|
2002-09-30 21:36:31 +04:00
|
|
|
static int config_initialized; /* config_init() has been called. */
|
|
|
|
|
2003-01-01 02:59:11 +03:00
|
|
|
static int config_do_twiddle;
|
|
|
|
|
Extract common code from i386, xen, and sparc64, creating
config_handle_wedges() and read_disk_sectors(). On x86, handle_wedges()
is a thin wrapper for config_handle_wedges(). Share opendisk()
across architectures.
Add kernel code in support of specifying a root partition by wedge
name. E.g., root specifications "wedge:wd0a", "wedge:David's Root
Volume" are possible. (Patches for config(1) coming soon.)
In support of moving disks between architectures (esp. i386 <->
evbmips), I've written a routine convertdisklabel() that ensures
that the raw partition is at RAW_DISK by following these steps:
0 If we have read a disklabel that has a RAW_PART with
p_offset == 0 and p_size != 0, then use that raw partition.
1 If we have read a disklabel that has both partitions 'c'
and 'd', and RAW_PART has p_offset != 0 or p_size == 0,
but the other partition is suitable for a raw partition
(p_offset == 0, p_size != 0), then swap the two partitions
and use the new raw partition.
2 If the architecture's raw partition is 'd', and if there
is no partition 'd', but there is a partition 'c' that
is suitable for a raw partition, then copy partition 'c'
to partition 'd'.
3 Determine the drive's last sector, using either the
d_secperunit the drive reported, or by guessing (0x1fffffff).
If we cannot read the drive's last sector, then fail.
4 If we have read a disklabel that has no partition slot
RAW_PART, then create a partition RAW_PART. Make it span
the whole drive.
5 If there are fewer than MAXPARTITIONS partitions,
then "slide" the unsuitable raw partition RAW_PART, and
subsequent partitions, into partition slots RAW_PART+1
and subsequent slots. Create a raw partition at RAW_PART.
Make it span the whole drive.
The convertdisklabel() procedure can probably stand to be simplified,
but it ought to deal with all but an extraordinarily broken disklabel,
now.
i386: compiled and tested, sparc64: compiled, evbmips: compiled.
2007-06-24 05:43:34 +04:00
|
|
|
struct vnode *
|
|
|
|
opendisk(struct device *dv)
|
|
|
|
{
|
|
|
|
int bmajor, bminor;
|
|
|
|
struct vnode *tmpvn;
|
|
|
|
int error;
|
|
|
|
dev_t dev;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Lookup major number for disk block device.
|
|
|
|
*/
|
|
|
|
bmajor = devsw_name2blk(device_xname(dv), NULL, 0);
|
|
|
|
if (bmajor == -1)
|
|
|
|
return NULL;
|
|
|
|
|
|
|
|
bminor = minor(device_unit(dv));
|
|
|
|
/*
|
|
|
|
* Fake a temporary vnode for the disk, open it, and read
|
|
|
|
* and hash the sectors.
|
|
|
|
*/
|
|
|
|
dev = device_is_a(dv, "dk") ? makedev(bmajor, bminor) :
|
|
|
|
MAKEDISKDEV(bmajor, bminor, RAW_PART);
|
|
|
|
if (bdevvp(dev, &tmpvn))
|
|
|
|
panic("%s: can't alloc vnode for %s", __func__,
|
|
|
|
device_xname(dv));
|
2007-11-26 22:01:26 +03:00
|
|
|
error = VOP_OPEN(tmpvn, FREAD, NOCRED);
|
Extract common code from i386, xen, and sparc64, creating
config_handle_wedges() and read_disk_sectors(). On x86, handle_wedges()
is a thin wrapper for config_handle_wedges(). Share opendisk()
across architectures.
Add kernel code in support of specifying a root partition by wedge
name. E.g., root specifications "wedge:wd0a", "wedge:David's Root
Volume" are possible. (Patches for config(1) coming soon.)
In support of moving disks between architectures (esp. i386 <->
evbmips), I've written a routine convertdisklabel() that ensures
that the raw partition is at RAW_DISK by following these steps:
0 If we have read a disklabel that has a RAW_PART with
p_offset == 0 and p_size != 0, then use that raw partition.
1 If we have read a disklabel that has both partitions 'c'
and 'd', and RAW_PART has p_offset != 0 or p_size == 0,
but the other partition is suitable for a raw partition
(p_offset == 0, p_size != 0), then swap the two partitions
and use the new raw partition.
2 If the architecture's raw partition is 'd', and if there
is no partition 'd', but there is a partition 'c' that
is suitable for a raw partition, then copy partition 'c'
to partition 'd'.
3 Determine the drive's last sector, using either the
d_secperunit the drive reported, or by guessing (0x1fffffff).
If we cannot read the drive's last sector, then fail.
4 If we have read a disklabel that has no partition slot
RAW_PART, then create a partition RAW_PART. Make it span
the whole drive.
5 If there are fewer than MAXPARTITIONS partitions,
then "slide" the unsuitable raw partition RAW_PART, and
subsequent partitions, into partition slots RAW_PART+1
and subsequent slots. Create a raw partition at RAW_PART.
Make it span the whole drive.
The convertdisklabel() procedure can probably stand to be simplified,
but it ought to deal with all but an extraordinarily broken disklabel,
now.
i386: compiled and tested, sparc64: compiled, evbmips: compiled.
2007-06-24 05:43:34 +04:00
|
|
|
if (error) {
|
|
|
|
#ifndef DEBUG
|
|
|
|
/*
|
|
|
|
* Ignore errors caused by missing device, partition,
|
|
|
|
* or medium.
|
|
|
|
*/
|
|
|
|
if (error != ENXIO && error != ENODEV)
|
|
|
|
#endif
|
|
|
|
printf("%s: can't open dev %s (%d)\n",
|
|
|
|
__func__, device_xname(dv), error);
|
|
|
|
vput(tmpvn);
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
return tmpvn;
|
|
|
|
}
|
|
|
|
|
|
|
|
int
|
|
|
|
config_handle_wedges(struct device *dv, int par)
|
|
|
|
{
|
|
|
|
struct dkwedge_list wl;
|
|
|
|
struct dkwedge_info *wi;
|
|
|
|
struct vnode *vn;
|
|
|
|
char diskname[16];
|
|
|
|
int i, error;
|
|
|
|
|
|
|
|
if ((vn = opendisk(dv)) == NULL)
|
|
|
|
return -1;
|
|
|
|
|
|
|
|
wl.dkwl_bufsize = sizeof(*wi) * 16;
|
|
|
|
wl.dkwl_buf = wi = malloc(wl.dkwl_bufsize, M_TEMP, M_WAITOK);
|
|
|
|
|
2007-11-26 22:01:26 +03:00
|
|
|
error = VOP_IOCTL(vn, DIOCLWEDGES, &wl, FREAD, NOCRED);
|
|
|
|
VOP_CLOSE(vn, FREAD, NOCRED);
|
Extract common code from i386, xen, and sparc64, creating
config_handle_wedges() and read_disk_sectors(). On x86, handle_wedges()
is a thin wrapper for config_handle_wedges(). Share opendisk()
across architectures.
Add kernel code in support of specifying a root partition by wedge
name. E.g., root specifications "wedge:wd0a", "wedge:David's Root
Volume" are possible. (Patches for config(1) coming soon.)
In support of moving disks between architectures (esp. i386 <->
evbmips), I've written a routine convertdisklabel() that ensures
that the raw partition is at RAW_DISK by following these steps:
0 If we have read a disklabel that has a RAW_PART with
p_offset == 0 and p_size != 0, then use that raw partition.
1 If we have read a disklabel that has both partitions 'c'
and 'd', and RAW_PART has p_offset != 0 or p_size == 0,
but the other partition is suitable for a raw partition
(p_offset == 0, p_size != 0), then swap the two partitions
and use the new raw partition.
2 If the architecture's raw partition is 'd', and if there
is no partition 'd', but there is a partition 'c' that
is suitable for a raw partition, then copy partition 'c'
to partition 'd'.
3 Determine the drive's last sector, using either the
d_secperunit the drive reported, or by guessing (0x1fffffff).
If we cannot read the drive's last sector, then fail.
4 If we have read a disklabel that has no partition slot
RAW_PART, then create a partition RAW_PART. Make it span
the whole drive.
5 If there are fewer than MAXPARTITIONS partitions,
then "slide" the unsuitable raw partition RAW_PART, and
subsequent partitions, into partition slots RAW_PART+1
and subsequent slots. Create a raw partition at RAW_PART.
Make it span the whole drive.
The convertdisklabel() procedure can probably stand to be simplified,
but it ought to deal with all but an extraordinarily broken disklabel,
now.
i386: compiled and tested, sparc64: compiled, evbmips: compiled.
2007-06-24 05:43:34 +04:00
|
|
|
vput(vn);
|
|
|
|
if (error) {
|
|
|
|
#ifdef DEBUG_WEDGE
|
|
|
|
printf("%s: List wedges returned %d\n",
|
|
|
|
device_xname(dv), error);
|
|
|
|
#endif
|
|
|
|
free(wi, M_TEMP);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
#ifdef DEBUG_WEDGE
|
|
|
|
printf("%s: Returned %u(%u) wedges\n", device_xname(dv),
|
|
|
|
wl.dkwl_nwedges, wl.dkwl_ncopied);
|
|
|
|
#endif
|
|
|
|
snprintf(diskname, sizeof(diskname), "%s%c", device_xname(dv),
|
|
|
|
par + 'a');
|
|
|
|
|
|
|
|
for (i = 0; i < wl.dkwl_ncopied; i++) {
|
|
|
|
#ifdef DEBUG_WEDGE
|
|
|
|
printf("%s: Looking for %s in %s\n",
|
|
|
|
device_xname(dv), diskname, wi[i].dkw_wname);
|
|
|
|
#endif
|
|
|
|
if (strcmp(wi[i].dkw_wname, diskname) == 0)
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (i == wl.dkwl_ncopied) {
|
|
|
|
#ifdef DEBUG_WEDGE
|
|
|
|
printf("%s: Cannot find wedge with parent %s\n",
|
|
|
|
device_xname(dv), diskname);
|
|
|
|
#endif
|
|
|
|
free(wi, M_TEMP);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
#ifdef DEBUG_WEDGE
|
|
|
|
printf("%s: Setting boot wedge %s (%s) at %llu %llu\n",
|
|
|
|
device_xname(dv), wi[i].dkw_devname, wi[i].dkw_wname,
|
|
|
|
(unsigned long long)wi[i].dkw_offset,
|
|
|
|
(unsigned long long)wi[i].dkw_size);
|
|
|
|
#endif
|
|
|
|
dkwedge_set_bootwedge(dv, wi[i].dkw_offset, wi[i].dkw_size);
|
|
|
|
free(wi, M_TEMP);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
1996-04-04 04:25:44 +04:00
|
|
|
/*
|
2002-09-30 21:36:31 +04:00
|
|
|
* Initialize the autoconfiguration data structures. Normally this
|
|
|
|
* is done by configure(), but some platforms need to do this very
|
|
|
|
* early (to e.g. initialize the console).
|
1996-04-04 04:25:44 +04:00
|
|
|
*/
|
|
|
|
void
|
2002-09-30 21:36:31 +04:00
|
|
|
config_init(void)
|
1996-04-04 04:25:44 +04:00
|
|
|
{
|
2002-10-04 05:50:53 +04:00
|
|
|
const struct cfattachinit *cfai;
|
|
|
|
int i, j;
|
1996-04-04 04:25:44 +04:00
|
|
|
|
2002-09-30 21:36:31 +04:00
|
|
|
if (config_initialized)
|
|
|
|
return;
|
|
|
|
|
2002-09-27 09:45:03 +04:00
|
|
|
/* allcfdrivers is statically initialized. */
|
2002-10-04 05:50:53 +04:00
|
|
|
for (i = 0; cfdriver_list_initial[i] != NULL; i++) {
|
2002-09-27 06:24:06 +04:00
|
|
|
if (config_cfdriver_attach(cfdriver_list_initial[i]) != 0)
|
|
|
|
panic("configure: duplicate `%s' drivers",
|
|
|
|
cfdriver_list_initial[i]->cd_name);
|
2002-10-04 05:50:53 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
for (cfai = &cfattachinit[0]; cfai->cfai_name != NULL; cfai++) {
|
|
|
|
for (j = 0; cfai->cfai_list[j] != NULL; j++) {
|
|
|
|
if (config_cfattach_attach(cfai->cfai_name,
|
|
|
|
cfai->cfai_list[j]) != 0)
|
|
|
|
panic("configure: duplicate `%s' attachment "
|
|
|
|
"of `%s' driver",
|
|
|
|
cfai->cfai_list[j]->ca_name,
|
|
|
|
cfai->cfai_name);
|
|
|
|
}
|
|
|
|
}
|
2002-09-24 03:16:06 +04:00
|
|
|
|
|
|
|
initcftable.ct_cfdata = cfdata;
|
|
|
|
TAILQ_INSERT_TAIL(&allcftables, &initcftable, ct_list);
|
|
|
|
|
2002-09-30 21:36:31 +04:00
|
|
|
config_initialized = 1;
|
|
|
|
}
|
|
|
|
|
2007-12-16 23:49:52 +03:00
|
|
|
void
|
|
|
|
config_deferred(device_t dev)
|
|
|
|
{
|
|
|
|
config_process_deferred(&deferred_config_queue, dev);
|
|
|
|
config_process_deferred(&interrupt_config_queue, dev);
|
|
|
|
}
|
|
|
|
|
2002-09-30 21:36:31 +04:00
|
|
|
/*
|
|
|
|
* Configure the system's hardware.
|
|
|
|
*/
|
|
|
|
void
|
|
|
|
configure(void)
|
|
|
|
{
|
2003-01-01 02:59:11 +03:00
|
|
|
int errcnt;
|
2002-09-30 21:36:31 +04:00
|
|
|
|
|
|
|
/* Initialize data structures. */
|
|
|
|
config_init();
|
2007-12-09 23:27:42 +03:00
|
|
|
pmf_init();
|
2002-09-30 21:36:31 +04:00
|
|
|
|
2001-07-01 06:56:20 +04:00
|
|
|
#ifdef USERCONF
|
|
|
|
if (boothowto & RB_USERCONF)
|
|
|
|
user_config();
|
|
|
|
#endif
|
|
|
|
|
2003-01-01 02:59:11 +03:00
|
|
|
if ((boothowto & (AB_SILENT|AB_VERBOSE)) == AB_SILENT) {
|
|
|
|
config_do_twiddle = 1;
|
|
|
|
printf_nolog("Detecting hardware...");
|
|
|
|
}
|
|
|
|
|
1999-09-15 22:10:33 +04:00
|
|
|
/*
|
|
|
|
* Do the machine-dependent portion of autoconfiguration. This
|
|
|
|
* sets the configuration machinery here in motion by "finding"
|
|
|
|
* the root bus. When this function returns, we expect interrupts
|
|
|
|
* to be enabled.
|
|
|
|
*/
|
|
|
|
cpu_configure();
|
1999-09-18 00:11:56 +04:00
|
|
|
|
2007-07-21 02:15:47 +04:00
|
|
|
/* Initialize callouts, part 2. */
|
|
|
|
callout_startup2();
|
|
|
|
|
1999-09-18 00:11:56 +04:00
|
|
|
/*
|
|
|
|
* Now that we've found all the hardware, start the real time
|
|
|
|
* and statistics clocks.
|
|
|
|
*/
|
|
|
|
initclocks();
|
|
|
|
|
|
|
|
cold = 0; /* clocks are running, we're warm now! */
|
1999-09-15 23:37:08 +04:00
|
|
|
|
2007-11-14 22:45:44 +03:00
|
|
|
#if defined(MULTIPROCESSOR)
|
|
|
|
/* Boot the secondary processors. */
|
|
|
|
cpu_boot_secondary_processors();
|
|
|
|
#endif
|
|
|
|
|
1999-09-15 23:37:08 +04:00
|
|
|
/*
|
|
|
|
* Now callback to finish configuration for devices which want
|
|
|
|
* to do this once interrupts are enabled.
|
|
|
|
*/
|
|
|
|
config_process_deferred(&interrupt_config_queue, NULL);
|
2003-01-01 02:59:11 +03:00
|
|
|
|
|
|
|
errcnt = aprint_get_error_count();
|
|
|
|
if ((boothowto & (AB_QUIET|AB_SILENT)) != 0 &&
|
|
|
|
(boothowto & AB_VERBOSE) == 0) {
|
|
|
|
if (config_do_twiddle) {
|
|
|
|
config_do_twiddle = 0;
|
|
|
|
printf_nolog("done.\n");
|
|
|
|
}
|
|
|
|
if (errcnt != 0) {
|
|
|
|
printf("WARNING: %d error%s while detecting hardware; "
|
|
|
|
"check system log.\n", errcnt,
|
|
|
|
errcnt == 1 ? "" : "s");
|
|
|
|
}
|
|
|
|
}
|
1996-04-04 04:25:44 +04:00
|
|
|
}
|
|
|
|
|
2002-09-27 06:24:06 +04:00
|
|
|
/*
|
|
|
|
* Add a cfdriver to the system.
|
|
|
|
*/
|
|
|
|
int
|
|
|
|
config_cfdriver_attach(struct cfdriver *cd)
|
|
|
|
{
|
|
|
|
struct cfdriver *lcd;
|
|
|
|
|
|
|
|
/* Make sure this driver isn't already in the system. */
|
|
|
|
LIST_FOREACH(lcd, &allcfdrivers, cd_list) {
|
|
|
|
if (STREQ(lcd->cd_name, cd->cd_name))
|
|
|
|
return (EEXIST);
|
|
|
|
}
|
|
|
|
|
2002-10-04 05:50:53 +04:00
|
|
|
LIST_INIT(&cd->cd_attach);
|
2002-09-27 06:24:06 +04:00
|
|
|
LIST_INSERT_HEAD(&allcfdrivers, cd, cd_list);
|
|
|
|
|
|
|
|
return (0);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Remove a cfdriver from the system.
|
|
|
|
*/
|
|
|
|
int
|
|
|
|
config_cfdriver_detach(struct cfdriver *cd)
|
|
|
|
{
|
|
|
|
int i;
|
|
|
|
|
|
|
|
/* Make sure there are no active instances. */
|
|
|
|
for (i = 0; i < cd->cd_ndevs; i++) {
|
|
|
|
if (cd->cd_devs[i] != NULL)
|
|
|
|
return (EBUSY);
|
|
|
|
}
|
|
|
|
|
2002-10-04 05:50:53 +04:00
|
|
|
/* ...and no attachments loaded. */
|
|
|
|
if (LIST_EMPTY(&cd->cd_attach) == 0)
|
|
|
|
return (EBUSY);
|
|
|
|
|
2002-09-27 06:24:06 +04:00
|
|
|
LIST_REMOVE(cd, cd_list);
|
|
|
|
|
|
|
|
KASSERT(cd->cd_devs == NULL);
|
|
|
|
|
|
|
|
return (0);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Look up a cfdriver by name.
|
|
|
|
*/
|
2002-10-20 06:26:59 +04:00
|
|
|
struct cfdriver *
|
2002-09-27 06:24:06 +04:00
|
|
|
config_cfdriver_lookup(const char *name)
|
|
|
|
{
|
|
|
|
struct cfdriver *cd;
|
|
|
|
|
|
|
|
LIST_FOREACH(cd, &allcfdrivers, cd_list) {
|
|
|
|
if (STREQ(cd->cd_name, name))
|
|
|
|
return (cd);
|
|
|
|
}
|
|
|
|
|
|
|
|
return (NULL);
|
|
|
|
}
|
|
|
|
|
2002-10-04 05:50:53 +04:00
|
|
|
/*
|
|
|
|
* Add a cfattach to the specified driver.
|
|
|
|
*/
|
|
|
|
int
|
|
|
|
config_cfattach_attach(const char *driver, struct cfattach *ca)
|
|
|
|
{
|
|
|
|
struct cfattach *lca;
|
|
|
|
struct cfdriver *cd;
|
|
|
|
|
|
|
|
cd = config_cfdriver_lookup(driver);
|
|
|
|
if (cd == NULL)
|
|
|
|
return (ESRCH);
|
|
|
|
|
|
|
|
/* Make sure this attachment isn't already on this driver. */
|
|
|
|
LIST_FOREACH(lca, &cd->cd_attach, ca_list) {
|
|
|
|
if (STREQ(lca->ca_name, ca->ca_name))
|
|
|
|
return (EEXIST);
|
|
|
|
}
|
|
|
|
|
|
|
|
LIST_INSERT_HEAD(&cd->cd_attach, ca, ca_list);
|
|
|
|
|
|
|
|
return (0);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Remove a cfattach from the specified driver.
|
|
|
|
*/
|
|
|
|
int
|
|
|
|
config_cfattach_detach(const char *driver, struct cfattach *ca)
|
|
|
|
{
|
|
|
|
struct cfdriver *cd;
|
2005-12-20 07:39:36 +03:00
|
|
|
device_t dev;
|
2002-10-04 05:50:53 +04:00
|
|
|
int i;
|
|
|
|
|
|
|
|
cd = config_cfdriver_lookup(driver);
|
|
|
|
if (cd == NULL)
|
|
|
|
return (ESRCH);
|
|
|
|
|
|
|
|
/* Make sure there are no active instances. */
|
|
|
|
for (i = 0; i < cd->cd_ndevs; i++) {
|
|
|
|
if ((dev = cd->cd_devs[i]) == NULL)
|
|
|
|
continue;
|
2002-10-09 06:59:55 +04:00
|
|
|
if (dev->dv_cfattach == ca)
|
2002-10-04 05:50:53 +04:00
|
|
|
return (EBUSY);
|
|
|
|
}
|
|
|
|
|
|
|
|
LIST_REMOVE(ca, ca_list);
|
|
|
|
|
|
|
|
return (0);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Look up a cfattach by name.
|
|
|
|
*/
|
|
|
|
static struct cfattach *
|
|
|
|
config_cfattach_lookup_cd(struct cfdriver *cd, const char *atname)
|
|
|
|
{
|
|
|
|
struct cfattach *ca;
|
|
|
|
|
|
|
|
LIST_FOREACH(ca, &cd->cd_attach, ca_list) {
|
|
|
|
if (STREQ(ca->ca_name, atname))
|
|
|
|
return (ca);
|
|
|
|
}
|
|
|
|
|
|
|
|
return (NULL);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Look up a cfattach by driver/attachment name.
|
|
|
|
*/
|
|
|
|
struct cfattach *
|
|
|
|
config_cfattach_lookup(const char *name, const char *atname)
|
|
|
|
{
|
|
|
|
struct cfdriver *cd;
|
|
|
|
|
|
|
|
cd = config_cfdriver_lookup(name);
|
|
|
|
if (cd == NULL)
|
|
|
|
return (NULL);
|
|
|
|
|
|
|
|
return (config_cfattach_lookup_cd(cd, atname));
|
|
|
|
}
|
|
|
|
|
1993-08-13 17:19:41 +04:00
|
|
|
/*
|
|
|
|
* Apply the matching function and choose the best. This is used
|
|
|
|
* a few times and we want to keep the code small.
|
|
|
|
*/
|
1994-11-04 09:40:11 +03:00
|
|
|
static void
|
2005-12-20 07:39:36 +03:00
|
|
|
mapply(struct matchinfo *m, cfdata_t cf)
|
1993-08-13 17:19:41 +04:00
|
|
|
{
|
2000-03-28 21:30:10 +04:00
|
|
|
int pri;
|
1993-08-13 17:19:41 +04:00
|
|
|
|
2005-08-26 18:20:40 +04:00
|
|
|
if (m->fn != NULL) {
|
|
|
|
pri = (*m->fn)(m->parent, cf, m->locs, m->aux);
|
2004-08-18 02:13:18 +04:00
|
|
|
} else {
|
2005-08-29 23:13:48 +04:00
|
|
|
pri = config_match(m->parent, cf, m->aux);
|
1993-08-16 03:01:58 +04:00
|
|
|
}
|
1993-08-13 17:19:41 +04:00
|
|
|
if (pri > m->pri) {
|
1996-12-05 03:09:10 +03:00
|
|
|
m->match = cf;
|
1993-08-13 17:19:41 +04:00
|
|
|
m->pri = pri;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2005-08-26 02:17:19 +04:00
|
|
|
int
|
2005-12-20 07:39:36 +03:00
|
|
|
config_stdsubmatch(device_t parent, cfdata_t cf, const int *locs, void *aux)
|
2005-08-26 02:17:19 +04:00
|
|
|
{
|
|
|
|
const struct cfiattrdata *ci;
|
|
|
|
const struct cflocdesc *cl;
|
|
|
|
int nlocs, i;
|
|
|
|
|
|
|
|
ci = cfiattr_lookup(cf->cf_pspec->cfp_iattr, parent->dv_cfdriver);
|
|
|
|
KASSERT(ci);
|
|
|
|
nlocs = ci->ci_loclen;
|
|
|
|
for (i = 0; i < nlocs; i++) {
|
|
|
|
cl = &ci->ci_locdesc[i];
|
|
|
|
/* !cld_defaultstr means no default value */
|
|
|
|
if ((!(cl->cld_defaultstr)
|
|
|
|
|| (cf->cf_loc[i] != cl->cld_default))
|
|
|
|
&& cf->cf_loc[i] != locs[i])
|
|
|
|
return (0);
|
|
|
|
}
|
|
|
|
|
|
|
|
return (config_match(parent, cf, aux));
|
|
|
|
}
|
|
|
|
|
2004-08-30 13:48:05 +04:00
|
|
|
/*
|
2005-08-25 19:06:28 +04:00
|
|
|
* Helper function: check whether the driver supports the interface attribute
|
|
|
|
* and return its descriptor structure.
|
2004-08-30 13:48:05 +04:00
|
|
|
*/
|
2005-08-25 19:06:28 +04:00
|
|
|
static const struct cfiattrdata *
|
|
|
|
cfdriver_get_iattr(const struct cfdriver *cd, const char *ia)
|
2004-08-30 13:48:05 +04:00
|
|
|
{
|
2005-08-25 19:06:28 +04:00
|
|
|
const struct cfiattrdata * const *cpp;
|
2004-08-30 13:48:05 +04:00
|
|
|
|
|
|
|
if (cd->cd_attrs == NULL)
|
|
|
|
return (0);
|
|
|
|
|
|
|
|
for (cpp = cd->cd_attrs; *cpp; cpp++) {
|
2005-08-25 19:06:28 +04:00
|
|
|
if (STREQ((*cpp)->ci_name, ia)) {
|
2004-08-30 13:48:05 +04:00
|
|
|
/* Match. */
|
2005-08-25 19:06:28 +04:00
|
|
|
return (*cpp);
|
2004-08-30 13:48:05 +04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
return (0);
|
|
|
|
}
|
|
|
|
|
2005-08-25 19:06:28 +04:00
|
|
|
/*
|
|
|
|
* Lookup an interface attribute description by name.
|
|
|
|
* If the driver is given, consider only its supported attributes.
|
|
|
|
*/
|
|
|
|
const struct cfiattrdata *
|
|
|
|
cfiattr_lookup(const char *name, const struct cfdriver *cd)
|
|
|
|
{
|
|
|
|
const struct cfdriver *d;
|
|
|
|
const struct cfiattrdata *ia;
|
|
|
|
|
|
|
|
if (cd)
|
|
|
|
return (cfdriver_get_iattr(cd, name));
|
|
|
|
|
|
|
|
LIST_FOREACH(d, &allcfdrivers, cd_list) {
|
|
|
|
ia = cfdriver_get_iattr(d, name);
|
|
|
|
if (ia)
|
|
|
|
return (ia);
|
|
|
|
}
|
|
|
|
return (0);
|
|
|
|
}
|
|
|
|
|
2002-09-26 08:07:35 +04:00
|
|
|
/*
|
|
|
|
* Determine if `parent' is a potential parent for a device spec based
|
|
|
|
* on `cfp'.
|
|
|
|
*/
|
|
|
|
static int
|
2005-12-20 07:39:36 +03:00
|
|
|
cfparent_match(const device_t parent, const struct cfparent *cfp)
|
2002-09-26 08:07:35 +04:00
|
|
|
{
|
2002-09-27 06:24:06 +04:00
|
|
|
struct cfdriver *pcd;
|
2002-09-26 08:07:35 +04:00
|
|
|
|
2002-09-27 10:12:55 +04:00
|
|
|
/* We don't match root nodes here. */
|
|
|
|
if (cfp == NULL)
|
|
|
|
return (0);
|
|
|
|
|
2002-10-09 06:59:55 +04:00
|
|
|
pcd = parent->dv_cfdriver;
|
2002-09-27 06:24:06 +04:00
|
|
|
KASSERT(pcd != NULL);
|
|
|
|
|
2002-09-26 08:07:35 +04:00
|
|
|
/*
|
|
|
|
* First, ensure this parent has the correct interface
|
|
|
|
* attribute.
|
|
|
|
*/
|
2005-08-25 19:06:28 +04:00
|
|
|
if (!cfdriver_get_iattr(pcd, cfp->cfp_iattr))
|
2004-08-30 13:48:05 +04:00
|
|
|
return (0);
|
2002-09-26 08:07:35 +04:00
|
|
|
|
|
|
|
/*
|
|
|
|
* If no specific parent device instance was specified (i.e.
|
|
|
|
* we're attaching to the attribute only), we're done!
|
|
|
|
*/
|
|
|
|
if (cfp->cfp_parent == NULL)
|
|
|
|
return (1);
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Check the parent device's name.
|
|
|
|
*/
|
2002-09-27 10:30:05 +04:00
|
|
|
if (STREQ(pcd->cd_name, cfp->cfp_parent) == 0)
|
2002-09-26 08:07:35 +04:00
|
|
|
return (0); /* not the same parent */
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Make sure the unit number matches.
|
|
|
|
*/
|
2002-10-09 06:59:55 +04:00
|
|
|
if (cfp->cfp_unit == DVUNIT_ANY || /* wildcard */
|
2002-09-26 08:07:35 +04:00
|
|
|
cfp->cfp_unit == parent->dv_unit)
|
|
|
|
return (1);
|
|
|
|
|
|
|
|
/* Unit numbers don't match. */
|
|
|
|
return (0);
|
|
|
|
}
|
|
|
|
|
2004-08-18 02:13:18 +04:00
|
|
|
/*
|
|
|
|
* Helper for config_cfdata_attach(): check all devices whether it could be
|
|
|
|
* parent any attachment in the config data table passed, and rescan.
|
|
|
|
*/
|
|
|
|
static void
|
|
|
|
rescan_with_cfdata(const struct cfdata *cf)
|
|
|
|
{
|
2005-12-20 07:39:36 +03:00
|
|
|
device_t d;
|
2004-08-18 02:13:18 +04:00
|
|
|
const struct cfdata *cf1;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* "alldevs" is likely longer than an LKM's cfdata, so make it
|
|
|
|
* the outer loop.
|
|
|
|
*/
|
|
|
|
TAILQ_FOREACH(d, &alldevs, dv_list) {
|
|
|
|
|
|
|
|
if (!(d->dv_cfattach->ca_rescan))
|
|
|
|
continue;
|
|
|
|
|
|
|
|
for (cf1 = cf; cf1->cf_name; cf1++) {
|
|
|
|
|
|
|
|
if (!cfparent_match(d, cf1->cf_pspec))
|
|
|
|
continue;
|
|
|
|
|
|
|
|
(*d->dv_cfattach->ca_rescan)(d,
|
|
|
|
cf1->cf_pspec->cfp_iattr, cf1->cf_loc);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Attach a supplemental config data table and rescan potential
|
|
|
|
* parent devices if required.
|
|
|
|
*/
|
|
|
|
int
|
2005-12-20 07:39:36 +03:00
|
|
|
config_cfdata_attach(cfdata_t cf, int scannow)
|
2004-08-18 02:13:18 +04:00
|
|
|
{
|
|
|
|
struct cftable *ct;
|
|
|
|
|
|
|
|
ct = malloc(sizeof(struct cftable), M_DEVBUF, M_WAITOK);
|
|
|
|
ct->ct_cfdata = cf;
|
|
|
|
TAILQ_INSERT_TAIL(&allcftables, ct, ct_list);
|
|
|
|
|
|
|
|
if (scannow)
|
|
|
|
rescan_with_cfdata(cf);
|
|
|
|
|
|
|
|
return (0);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Helper for config_cfdata_detach: check whether a device is
|
|
|
|
* found through any attachment in the config data table.
|
|
|
|
*/
|
|
|
|
static int
|
|
|
|
dev_in_cfdata(const struct device *d, const struct cfdata *cf)
|
|
|
|
{
|
|
|
|
const struct cfdata *cf1;
|
|
|
|
|
|
|
|
for (cf1 = cf; cf1->cf_name; cf1++)
|
|
|
|
if (d->dv_cfdata == cf1)
|
|
|
|
return (1);
|
|
|
|
|
|
|
|
return (0);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Detach a supplemental config data table. Detach all devices found
|
|
|
|
* through that table (and thus keeping references to it) before.
|
|
|
|
*/
|
|
|
|
int
|
2005-12-20 07:39:36 +03:00
|
|
|
config_cfdata_detach(cfdata_t cf)
|
2004-08-18 02:13:18 +04:00
|
|
|
{
|
2005-12-20 07:39:36 +03:00
|
|
|
device_t d;
|
2004-08-18 02:13:18 +04:00
|
|
|
int error;
|
|
|
|
struct cftable *ct;
|
|
|
|
|
|
|
|
again:
|
|
|
|
TAILQ_FOREACH(d, &alldevs, dv_list) {
|
|
|
|
if (dev_in_cfdata(d, cf)) {
|
|
|
|
error = config_detach(d, 0);
|
|
|
|
if (error) {
|
|
|
|
aprint_error("%s: unable to detach instance\n",
|
|
|
|
d->dv_xname);
|
|
|
|
return (error);
|
|
|
|
}
|
|
|
|
goto again;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
TAILQ_FOREACH(ct, &allcftables, ct_list) {
|
|
|
|
if (ct->ct_cfdata == cf) {
|
|
|
|
TAILQ_REMOVE(&allcftables, ct, ct_list);
|
|
|
|
free(ct, M_DEVBUF);
|
|
|
|
return (0);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/* not found -- shouldn't happen */
|
|
|
|
return (EINVAL);
|
|
|
|
}
|
|
|
|
|
2002-09-27 07:17:40 +04:00
|
|
|
/*
|
|
|
|
* Invoke the "match" routine for a cfdata entry on behalf of
|
|
|
|
* an external caller, usually a "submatch" routine.
|
|
|
|
*/
|
|
|
|
int
|
2005-12-20 07:39:36 +03:00
|
|
|
config_match(device_t parent, cfdata_t cf, void *aux)
|
2002-09-27 07:17:40 +04:00
|
|
|
{
|
2002-10-04 05:50:53 +04:00
|
|
|
struct cfattach *ca;
|
|
|
|
|
|
|
|
ca = config_cfattach_lookup(cf->cf_name, cf->cf_atname);
|
|
|
|
if (ca == NULL) {
|
|
|
|
/* No attachment for this entry, oh well. */
|
|
|
|
return (0);
|
|
|
|
}
|
2002-09-27 07:17:40 +04:00
|
|
|
|
2002-10-04 05:50:53 +04:00
|
|
|
return ((*ca->ca_match)(parent, cf, aux));
|
2002-09-27 07:17:40 +04:00
|
|
|
}
|
|
|
|
|
1993-08-13 17:19:41 +04:00
|
|
|
/*
|
|
|
|
* Iterate over all potential children of some device, calling the given
|
|
|
|
* function (default being the child's match function) for each one.
|
|
|
|
* Nonzero returns are matches; the highest value returned is considered
|
|
|
|
* the best match. Return the `found child' if we got a match, or NULL
|
|
|
|
* otherwise. The `aux' pointer is simply passed on through.
|
|
|
|
*
|
|
|
|
* Note that this function is designed so that it can be used to apply
|
|
|
|
* an arbitrary function to all potential children (its return value
|
|
|
|
* can be ignored).
|
|
|
|
*/
|
2005-12-20 07:39:36 +03:00
|
|
|
cfdata_t
|
|
|
|
config_search_loc(cfsubmatch_t fn, device_t parent,
|
2005-08-26 18:20:40 +04:00
|
|
|
const char *ifattr, const int *locs, void *aux)
|
2004-08-18 02:13:18 +04:00
|
|
|
{
|
|
|
|
struct cftable *ct;
|
2005-12-20 07:39:36 +03:00
|
|
|
cfdata_t cf;
|
2004-08-18 02:13:18 +04:00
|
|
|
struct matchinfo m;
|
|
|
|
|
|
|
|
KASSERT(config_initialized);
|
2005-08-25 19:06:28 +04:00
|
|
|
KASSERT(!ifattr || cfdriver_get_iattr(parent->dv_cfdriver, ifattr));
|
2004-08-18 02:13:18 +04:00
|
|
|
|
2005-08-26 18:20:40 +04:00
|
|
|
m.fn = fn;
|
1993-08-13 17:19:41 +04:00
|
|
|
m.parent = parent;
|
2005-08-26 18:20:40 +04:00
|
|
|
m.locs = locs;
|
1994-11-04 06:07:17 +03:00
|
|
|
m.aux = aux;
|
1996-12-05 03:09:10 +03:00
|
|
|
m.match = NULL;
|
1993-08-13 17:19:41 +04:00
|
|
|
m.pri = 0;
|
2002-09-24 03:16:06 +04:00
|
|
|
|
|
|
|
TAILQ_FOREACH(ct, &allcftables, ct_list) {
|
2002-09-27 06:24:06 +04:00
|
|
|
for (cf = ct->ct_cfdata; cf->cf_name; cf++) {
|
2004-08-18 02:13:18 +04:00
|
|
|
|
|
|
|
/* We don't match root nodes here. */
|
|
|
|
if (!cf->cf_pspec)
|
|
|
|
continue;
|
|
|
|
|
2002-09-24 03:16:06 +04:00
|
|
|
/*
|
|
|
|
* Skip cf if no longer eligible, otherwise scan
|
|
|
|
* through parents for one matching `parent', and
|
|
|
|
* try match function.
|
|
|
|
*/
|
|
|
|
if (cf->cf_fstate == FSTATE_FOUND)
|
|
|
|
continue;
|
|
|
|
if (cf->cf_fstate == FSTATE_DNOTFOUND ||
|
|
|
|
cf->cf_fstate == FSTATE_DSTAR)
|
|
|
|
continue;
|
2004-08-18 02:13:18 +04:00
|
|
|
|
|
|
|
/*
|
|
|
|
* If an interface attribute was specified,
|
|
|
|
* consider only children which attach to
|
|
|
|
* that attribute.
|
|
|
|
*/
|
|
|
|
if (ifattr && !STREQ(ifattr, cf->cf_pspec->cfp_iattr))
|
|
|
|
continue;
|
|
|
|
|
2002-09-26 08:07:35 +04:00
|
|
|
if (cfparent_match(parent, cf->cf_pspec))
|
|
|
|
mapply(&m, cf);
|
2002-09-24 03:16:06 +04:00
|
|
|
}
|
1993-08-13 17:19:41 +04:00
|
|
|
}
|
|
|
|
return (m.match);
|
|
|
|
}
|
|
|
|
|
2005-12-20 07:39:36 +03:00
|
|
|
cfdata_t
|
|
|
|
config_search_ia(cfsubmatch_t fn, device_t parent, const char *ifattr,
|
|
|
|
void *aux)
|
|
|
|
{
|
|
|
|
|
|
|
|
return (config_search_loc(fn, parent, ifattr, NULL, aux));
|
|
|
|
}
|
|
|
|
|
1993-08-13 17:19:41 +04:00
|
|
|
/*
|
|
|
|
* Find the given root device.
|
|
|
|
* This is much like config_search, but there is no parent.
|
2002-09-24 03:16:06 +04:00
|
|
|
* Don't bother with multiple cfdata tables; the root node
|
|
|
|
* must always be in the initial table.
|
1993-08-13 17:19:41 +04:00
|
|
|
*/
|
2005-12-20 07:39:36 +03:00
|
|
|
cfdata_t
|
2005-06-28 22:37:34 +04:00
|
|
|
config_rootsearch(cfsubmatch_t fn, const char *rootname, void *aux)
|
1993-08-13 17:19:41 +04:00
|
|
|
{
|
2005-12-20 07:39:36 +03:00
|
|
|
cfdata_t cf;
|
2003-03-16 11:09:58 +03:00
|
|
|
const short *p;
|
1993-08-13 17:19:41 +04:00
|
|
|
struct matchinfo m;
|
|
|
|
|
2005-08-26 18:20:40 +04:00
|
|
|
m.fn = fn;
|
1993-08-13 17:19:41 +04:00
|
|
|
m.parent = ROOT;
|
1994-11-04 06:07:17 +03:00
|
|
|
m.aux = aux;
|
1996-12-05 03:09:10 +03:00
|
|
|
m.match = NULL;
|
1993-08-13 17:19:41 +04:00
|
|
|
m.pri = 0;
|
2006-05-14 09:26:59 +04:00
|
|
|
m.locs = 0;
|
1993-08-13 17:19:41 +04:00
|
|
|
/*
|
|
|
|
* Look at root entries for matching name. We do not bother
|
|
|
|
* with found-state here since only one root should ever be
|
|
|
|
* searched (and it must be done first).
|
|
|
|
*/
|
|
|
|
for (p = cfroots; *p >= 0; p++) {
|
|
|
|
cf = &cfdata[*p];
|
2002-09-27 06:24:06 +04:00
|
|
|
if (strcmp(cf->cf_name, rootname) == 0)
|
1994-11-04 09:40:11 +03:00
|
|
|
mapply(&m, cf);
|
1993-08-13 17:19:41 +04:00
|
|
|
}
|
|
|
|
return (m.match);
|
|
|
|
}
|
|
|
|
|
2003-02-09 12:14:58 +03:00
|
|
|
static const char * const msgs[3] = { "", " not configured\n", " unsupported\n" };
|
1993-08-13 17:19:41 +04:00
|
|
|
|
|
|
|
/*
|
|
|
|
* The given `aux' argument describes a device that has been found
|
|
|
|
* on the given parent, but not necessarily configured. Locate the
|
1996-02-28 00:45:46 +03:00
|
|
|
* configuration data for that device (using the submatch function
|
|
|
|
* provided, or using candidates' cd_match configuration driver
|
|
|
|
* functions) and attach it, and return true. If the device was
|
1993-08-13 17:19:41 +04:00
|
|
|
* not configured, call the given `print' function and return 0.
|
|
|
|
*/
|
2005-12-20 07:39:36 +03:00
|
|
|
device_t
|
|
|
|
config_found_sm_loc(device_t parent,
|
2005-08-26 18:20:40 +04:00
|
|
|
const char *ifattr, const int *locs, void *aux,
|
2005-06-28 22:37:34 +04:00
|
|
|
cfprint_t print, cfsubmatch_t submatch)
|
2004-08-18 02:13:18 +04:00
|
|
|
{
|
2005-12-20 07:39:36 +03:00
|
|
|
cfdata_t cf;
|
2004-08-18 02:13:18 +04:00
|
|
|
|
2006-02-18 22:09:53 +03:00
|
|
|
#if defined(SPLASHSCREEN) && defined(SPLASHSCREEN_PROGRESS)
|
|
|
|
if (splash_progress_state)
|
|
|
|
splash_progress_update(splash_progress_state);
|
|
|
|
#endif
|
|
|
|
|
2005-08-26 18:20:40 +04:00
|
|
|
if ((cf = config_search_loc(submatch, parent, ifattr, locs, aux)))
|
|
|
|
return(config_attach_loc(parent, cf, locs, aux, print));
|
2004-08-18 02:13:18 +04:00
|
|
|
if (print) {
|
|
|
|
if (config_do_twiddle)
|
|
|
|
twiddle();
|
|
|
|
aprint_normal("%s", msgs[(*print)(aux, parent->dv_xname)]);
|
|
|
|
}
|
2006-02-18 22:09:53 +03:00
|
|
|
|
|
|
|
#if defined(SPLASHSCREEN) && defined(SPLASHSCREEN_PROGRESS)
|
|
|
|
if (splash_progress_state)
|
|
|
|
splash_progress_update(splash_progress_state);
|
|
|
|
#endif
|
|
|
|
|
2004-08-18 02:13:18 +04:00
|
|
|
return (NULL);
|
|
|
|
}
|
|
|
|
|
2005-12-20 07:39:36 +03:00
|
|
|
device_t
|
|
|
|
config_found_ia(device_t parent, const char *ifattr, void *aux,
|
|
|
|
cfprint_t print)
|
|
|
|
{
|
|
|
|
|
|
|
|
return (config_found_sm_loc(parent, ifattr, NULL, aux, print, NULL));
|
|
|
|
}
|
|
|
|
|
|
|
|
device_t
|
|
|
|
config_found(device_t parent, void *aux, cfprint_t print)
|
|
|
|
{
|
|
|
|
|
|
|
|
return (config_found_sm_loc(parent, NULL, NULL, aux, print, NULL));
|
|
|
|
}
|
|
|
|
|
1993-08-13 17:19:41 +04:00
|
|
|
/*
|
|
|
|
* As above, but for root devices.
|
|
|
|
*/
|
2005-12-20 07:39:36 +03:00
|
|
|
device_t
|
2000-06-02 05:48:50 +04:00
|
|
|
config_rootfound(const char *rootname, void *aux)
|
1993-08-13 17:19:41 +04:00
|
|
|
{
|
2005-12-20 07:39:36 +03:00
|
|
|
cfdata_t cf;
|
1996-12-05 03:09:10 +03:00
|
|
|
|
2005-06-28 22:37:34 +04:00
|
|
|
if ((cf = config_rootsearch((cfsubmatch_t)NULL, rootname, aux)) != NULL)
|
1996-12-05 03:09:10 +03:00
|
|
|
return (config_attach(ROOT, cf, aux, (cfprint_t)NULL));
|
2003-01-01 02:59:11 +03:00
|
|
|
aprint_error("root device %s not configured\n", rootname);
|
1996-04-04 10:06:18 +04:00
|
|
|
return (NULL);
|
1993-08-13 17:19:41 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
/* just like sprintf(buf, "%d") except that it works from the end */
|
|
|
|
static char *
|
2000-06-02 05:31:52 +04:00
|
|
|
number(char *ep, int n)
|
1993-08-13 17:19:41 +04:00
|
|
|
{
|
|
|
|
|
|
|
|
*--ep = 0;
|
|
|
|
while (n >= 10) {
|
|
|
|
*--ep = (n % 10) + '0';
|
|
|
|
n /= 10;
|
|
|
|
}
|
|
|
|
*--ep = n + '0';
|
|
|
|
return (ep);
|
|
|
|
}
|
|
|
|
|
2001-12-02 05:40:57 +03:00
|
|
|
/*
|
|
|
|
* Expand the size of the cd_devs array if necessary.
|
|
|
|
*/
|
2007-03-05 23:32:43 +03:00
|
|
|
static void
|
2001-12-02 05:40:57 +03:00
|
|
|
config_makeroom(int n, struct cfdriver *cd)
|
|
|
|
{
|
|
|
|
int old, new;
|
|
|
|
void **nsp;
|
|
|
|
|
|
|
|
if (n < cd->cd_ndevs)
|
|
|
|
return;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Need to expand the array.
|
|
|
|
*/
|
|
|
|
old = cd->cd_ndevs;
|
2001-12-10 03:33:04 +03:00
|
|
|
if (old == 0)
|
2006-10-02 06:59:38 +04:00
|
|
|
new = 4;
|
2001-12-02 05:40:57 +03:00
|
|
|
else
|
|
|
|
new = old * 2;
|
|
|
|
while (new <= n)
|
|
|
|
new *= 2;
|
|
|
|
cd->cd_ndevs = new;
|
|
|
|
nsp = malloc(new * sizeof(void *), M_DEVBUF,
|
2005-02-27 00:34:55 +03:00
|
|
|
cold ? M_NOWAIT : M_WAITOK);
|
2001-12-05 19:12:42 +03:00
|
|
|
if (nsp == NULL)
|
2001-12-02 05:40:57 +03:00
|
|
|
panic("config_attach: %sing dev array",
|
|
|
|
old != 0 ? "expand" : "creat");
|
|
|
|
memset(nsp + old, 0, (new - old) * sizeof(void *));
|
2001-12-10 03:33:04 +03:00
|
|
|
if (old != 0) {
|
2001-12-02 05:40:57 +03:00
|
|
|
memcpy(nsp, cd->cd_devs, old * sizeof(void *));
|
|
|
|
free(cd->cd_devs, M_DEVBUF);
|
|
|
|
}
|
|
|
|
cd->cd_devs = nsp;
|
|
|
|
}
|
|
|
|
|
2007-03-05 23:32:43 +03:00
|
|
|
static void
|
|
|
|
config_devlink(device_t dev)
|
|
|
|
{
|
|
|
|
struct cfdriver *cd = dev->dv_cfdriver;
|
|
|
|
|
|
|
|
/* put this device in the devices array */
|
|
|
|
config_makeroom(dev->dv_unit, cd);
|
|
|
|
if (cd->cd_devs[dev->dv_unit])
|
|
|
|
panic("config_attach: duplicate %s", dev->dv_xname);
|
|
|
|
cd->cd_devs[dev->dv_unit] = dev;
|
|
|
|
|
|
|
|
TAILQ_INSERT_TAIL(&alldevs, dev, dv_list); /* link up */
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
config_devunlink(device_t dev)
|
|
|
|
{
|
|
|
|
struct cfdriver *cd = dev->dv_cfdriver;
|
|
|
|
int i;
|
|
|
|
|
|
|
|
/* Unlink from device list. */
|
|
|
|
TAILQ_REMOVE(&alldevs, dev, dv_list);
|
|
|
|
|
|
|
|
/* Remove from cfdriver's array. */
|
|
|
|
cd->cd_devs[dev->dv_unit] = NULL;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* If the device now has no units in use, deallocate its softc array.
|
|
|
|
*/
|
|
|
|
for (i = 0; i < cd->cd_ndevs; i++)
|
|
|
|
if (cd->cd_devs[i] != NULL)
|
|
|
|
break;
|
|
|
|
if (i == cd->cd_ndevs) { /* nothing found; deallocate */
|
|
|
|
free(cd->cd_devs, M_DEVBUF);
|
|
|
|
cd->cd_devs = NULL;
|
|
|
|
cd->cd_ndevs = 0;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static device_t
|
|
|
|
config_devalloc(const device_t parent, const cfdata_t cf, const int *locs)
|
1996-12-05 03:09:10 +03:00
|
|
|
{
|
2000-03-28 21:30:10 +04:00
|
|
|
struct cfdriver *cd;
|
2002-10-04 05:50:53 +04:00
|
|
|
struct cfattach *ca;
|
2000-03-28 21:30:10 +04:00
|
|
|
size_t lname, lunit;
|
2000-06-02 05:48:50 +04:00
|
|
|
const char *xunit;
|
1996-12-05 03:09:10 +03:00
|
|
|
int myunit;
|
|
|
|
char num[10];
|
2007-03-05 23:32:43 +03:00
|
|
|
device_t dev;
|
2007-09-24 22:47:56 +04:00
|
|
|
void *dev_private;
|
2005-08-25 19:06:28 +04:00
|
|
|
const struct cfiattrdata *ia;
|
1996-12-05 03:09:10 +03:00
|
|
|
|
2002-09-27 06:24:06 +04:00
|
|
|
cd = config_cfdriver_lookup(cf->cf_name);
|
2007-03-05 23:32:43 +03:00
|
|
|
if (cd == NULL)
|
|
|
|
return (NULL);
|
2002-10-04 05:50:53 +04:00
|
|
|
|
|
|
|
ca = config_cfattach_lookup_cd(cd, cf->cf_atname);
|
2007-03-05 23:32:43 +03:00
|
|
|
if (ca == NULL)
|
|
|
|
return (NULL);
|
2002-10-04 05:50:53 +04:00
|
|
|
|
2007-09-24 22:47:56 +04:00
|
|
|
if ((ca->ca_flags & DVF_PRIV_ALLOC) == 0 &&
|
|
|
|
ca->ca_devsize < sizeof(struct device))
|
2007-03-05 23:32:43 +03:00
|
|
|
panic("config_devalloc");
|
2002-09-26 08:07:35 +04:00
|
|
|
|
2000-01-18 10:45:04 +03:00
|
|
|
#ifndef __BROKEN_CONFIG_UNIT_USAGE
|
avoid 'marching unit numbers' for cloning devices. (e.g., previously,
if you com* at pcmcia?, and com3 and com4 as pcmcia cards, and removed
and reinserted the card that was com3, it would become com5. if you then
removed and reinserted com4, it would become com6. etc.) Now, instead
of incrementing FSTATE_STAR configuration entries for a driver when
a cloning instance is attached, leave it alone, and scan the device softc
array (starting at the first cloning unit number) for units which are
available for use. This wastes a tiny bit of time (can require a linear
scan of the softc table for the device), but device attachment should be
relatively infrequent and the number of units of each type of device
is never particularly large anyway.
1999-12-30 04:03:43 +03:00
|
|
|
if (cf->cf_fstate == FSTATE_STAR) {
|
|
|
|
for (myunit = cf->cf_unit; myunit < cd->cd_ndevs; myunit++)
|
|
|
|
if (cd->cd_devs[myunit] == NULL)
|
|
|
|
break;
|
|
|
|
/*
|
|
|
|
* myunit is now the unit of the first NULL device pointer,
|
|
|
|
* or max(cd->cd_ndevs,cf->cf_unit).
|
|
|
|
*/
|
|
|
|
} else {
|
|
|
|
myunit = cf->cf_unit;
|
2007-03-05 23:32:43 +03:00
|
|
|
if (myunit < cd->cd_ndevs && cd->cd_devs[myunit] != NULL)
|
|
|
|
return (NULL);
|
|
|
|
}
|
2002-09-26 08:07:35 +04:00
|
|
|
#else
|
2000-01-18 10:45:04 +03:00
|
|
|
myunit = cf->cf_unit;
|
2002-09-26 08:07:35 +04:00
|
|
|
#endif /* ! __BROKEN_CONFIG_UNIT_USAGE */
|
1996-12-05 03:09:10 +03:00
|
|
|
|
|
|
|
/* compute length of name and decimal expansion of unit number */
|
|
|
|
lname = strlen(cd->cd_name);
|
1998-08-01 02:50:48 +04:00
|
|
|
xunit = number(&num[sizeof(num)], myunit);
|
|
|
|
lunit = &num[sizeof(num)] - xunit;
|
2002-07-10 23:04:09 +04:00
|
|
|
if (lname + lunit > sizeof(dev->dv_xname))
|
2007-03-05 23:32:43 +03:00
|
|
|
panic("config_devalloc: device name too long");
|
1996-12-05 03:09:10 +03:00
|
|
|
|
|
|
|
/* get memory for all device vars */
|
2007-09-24 22:47:56 +04:00
|
|
|
dev_private = malloc(ca->ca_devsize, M_DEVBUF,
|
|
|
|
M_ZERO | (cold ? M_NOWAIT : M_WAITOK));
|
|
|
|
if (dev_private == NULL)
|
2007-03-05 23:32:43 +03:00
|
|
|
panic("config_devalloc: memory allocation for device softc failed");
|
2007-09-24 22:47:56 +04:00
|
|
|
|
|
|
|
if ((ca->ca_flags & DVF_PRIV_ALLOC) != 0) {
|
|
|
|
dev = malloc(sizeof(struct device), M_DEVBUF,
|
|
|
|
M_ZERO | (cold ? M_NOWAIT : M_WAITOK));
|
|
|
|
} else {
|
|
|
|
dev = dev_private;
|
|
|
|
}
|
|
|
|
if (dev == NULL)
|
|
|
|
panic("config_devalloc: memory allocation for device_t failed");
|
2007-12-09 23:27:42 +03:00
|
|
|
|
1996-12-05 03:09:10 +03:00
|
|
|
dev->dv_class = cd->cd_class;
|
|
|
|
dev->dv_cfdata = cf;
|
2002-10-04 05:50:53 +04:00
|
|
|
dev->dv_cfdriver = cd;
|
|
|
|
dev->dv_cfattach = ca;
|
1996-12-05 03:09:10 +03:00
|
|
|
dev->dv_unit = myunit;
|
2007-12-09 23:27:42 +03:00
|
|
|
dev->dv_activity_count = 0;
|
|
|
|
dev->dv_activity_handlers = NULL;
|
2007-09-24 22:47:56 +04:00
|
|
|
dev->dv_private = dev_private;
|
Abolition of bcopy, ovbcopy, bcmp, and bzero, phase one.
bcopy(x, y, z) -> memcpy(y, x, z)
ovbcopy(x, y, z) -> memmove(y, x, z)
bcmp(x, y, z) -> memcmp(x, y, z)
bzero(x, y) -> memset(x, 0, y)
1998-08-04 08:03:10 +04:00
|
|
|
memcpy(dev->dv_xname, cd->cd_name, lname);
|
|
|
|
memcpy(dev->dv_xname + lname, xunit, lunit);
|
1996-12-05 03:09:10 +03:00
|
|
|
dev->dv_parent = parent;
|
2007-12-09 23:27:42 +03:00
|
|
|
if (parent != NULL)
|
|
|
|
dev->dv_depth = parent->dv_depth + 1;
|
|
|
|
else
|
|
|
|
dev->dv_depth = 0;
|
1998-11-17 11:38:07 +03:00
|
|
|
dev->dv_flags = DVF_ACTIVE; /* always initially active */
|
2007-09-24 22:47:56 +04:00
|
|
|
dev->dv_flags |= ca->ca_flags; /* inherit flags from class */
|
2005-08-25 22:35:38 +04:00
|
|
|
if (locs) {
|
2005-08-25 19:06:28 +04:00
|
|
|
KASSERT(parent); /* no locators at root */
|
|
|
|
ia = cfiattr_lookup(cf->cf_pspec->cfp_iattr,
|
|
|
|
parent->dv_cfdriver);
|
|
|
|
dev->dv_locators = malloc(ia->ci_loclen * sizeof(int),
|
2004-08-18 02:13:18 +04:00
|
|
|
M_DEVBUF, cold ? M_NOWAIT : M_WAITOK);
|
2005-08-25 22:35:38 +04:00
|
|
|
memcpy(dev->dv_locators, locs, ia->ci_loclen * sizeof(int));
|
2004-08-18 02:13:18 +04:00
|
|
|
}
|
2006-05-05 22:04:41 +04:00
|
|
|
dev->dv_properties = prop_dictionary_create();
|
|
|
|
KASSERT(dev->dv_properties != NULL);
|
1998-06-09 22:46:12 +04:00
|
|
|
|
2007-03-05 23:32:43 +03:00
|
|
|
return (dev);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
config_devdealloc(device_t dev)
|
|
|
|
{
|
|
|
|
|
|
|
|
KASSERT(dev->dv_properties != NULL);
|
|
|
|
prop_object_release(dev->dv_properties);
|
|
|
|
|
2007-12-09 23:27:42 +03:00
|
|
|
if (dev->dv_activity_handlers)
|
|
|
|
panic("config_devdealloc with registered handlers");
|
|
|
|
|
2007-03-05 23:32:43 +03:00
|
|
|
if (dev->dv_locators)
|
|
|
|
free(dev->dv_locators, M_DEVBUF);
|
|
|
|
|
2007-09-24 22:47:56 +04:00
|
|
|
if ((dev->dv_flags & DVF_PRIV_ALLOC) != 0)
|
|
|
|
free(dev->dv_private, M_DEVBUF);
|
|
|
|
|
2007-03-05 23:32:43 +03:00
|
|
|
free(dev, M_DEVBUF);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Attach a found device.
|
|
|
|
*/
|
|
|
|
device_t
|
|
|
|
config_attach_loc(device_t parent, cfdata_t cf,
|
|
|
|
const int *locs, void *aux, cfprint_t print)
|
|
|
|
{
|
|
|
|
device_t dev;
|
|
|
|
struct cftable *ct;
|
|
|
|
const char *drvname;
|
|
|
|
|
|
|
|
#if defined(SPLASHSCREEN) && defined(SPLASHSCREEN_PROGRESS)
|
|
|
|
if (splash_progress_state)
|
|
|
|
splash_progress_update(splash_progress_state);
|
|
|
|
#endif
|
|
|
|
|
|
|
|
dev = config_devalloc(parent, cf, locs);
|
|
|
|
if (!dev)
|
|
|
|
panic("config_attach: allocation of device softc failed");
|
|
|
|
|
|
|
|
/* XXX redundant - see below? */
|
|
|
|
if (cf->cf_fstate != FSTATE_STAR) {
|
|
|
|
KASSERT(cf->cf_fstate == FSTATE_NOTFOUND);
|
|
|
|
cf->cf_fstate = FSTATE_FOUND;
|
|
|
|
}
|
|
|
|
#ifdef __BROKEN_CONFIG_UNIT_USAGE
|
|
|
|
else
|
|
|
|
cf->cf_unit++;
|
|
|
|
#endif
|
|
|
|
|
|
|
|
config_devlink(dev);
|
|
|
|
|
2003-01-01 02:59:11 +03:00
|
|
|
if (config_do_twiddle)
|
|
|
|
twiddle();
|
|
|
|
else
|
|
|
|
aprint_naive("Found ");
|
|
|
|
/*
|
|
|
|
* We want the next two printfs for normal, verbose, and quiet,
|
|
|
|
* but not silent (in which case, we're twiddling, instead).
|
|
|
|
*/
|
|
|
|
if (parent == ROOT) {
|
2003-04-29 04:56:52 +04:00
|
|
|
aprint_naive("%s (root)", dev->dv_xname);
|
|
|
|
aprint_normal("%s (root)", dev->dv_xname);
|
2003-01-01 02:59:11 +03:00
|
|
|
} else {
|
2003-04-29 04:56:52 +04:00
|
|
|
aprint_naive("%s at %s", dev->dv_xname, parent->dv_xname);
|
|
|
|
aprint_normal("%s at %s", dev->dv_xname, parent->dv_xname);
|
1996-12-05 03:09:10 +03:00
|
|
|
if (print)
|
2000-06-02 05:48:50 +04:00
|
|
|
(void) (*print)(aux, NULL);
|
1996-12-05 03:09:10 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Before attaching, clobber any unfound devices that are
|
avoid 'marching unit numbers' for cloning devices. (e.g., previously,
if you com* at pcmcia?, and com3 and com4 as pcmcia cards, and removed
and reinserted the card that was com3, it would become com5. if you then
removed and reinserted com4, it would become com6. etc.) Now, instead
of incrementing FSTATE_STAR configuration entries for a driver when
a cloning instance is attached, leave it alone, and scan the device softc
array (starting at the first cloning unit number) for units which are
available for use. This wastes a tiny bit of time (can require a linear
scan of the softc table for the device), but device attachment should be
relatively infrequent and the number of units of each type of device
is never particularly large anyway.
1999-12-30 04:03:43 +03:00
|
|
|
* otherwise identical.
|
2007-03-05 23:32:43 +03:00
|
|
|
* XXX code above is redundant?
|
1996-12-05 03:09:10 +03:00
|
|
|
*/
|
2007-03-05 23:32:43 +03:00
|
|
|
drvname = dev->dv_cfdriver->cd_name;
|
2002-09-24 03:16:06 +04:00
|
|
|
TAILQ_FOREACH(ct, &allcftables, ct_list) {
|
2002-09-27 06:24:06 +04:00
|
|
|
for (cf = ct->ct_cfdata; cf->cf_name; cf++) {
|
2007-03-05 23:32:43 +03:00
|
|
|
if (STREQ(cf->cf_name, drvname) &&
|
2002-09-24 03:16:06 +04:00
|
|
|
cf->cf_unit == dev->dv_unit) {
|
|
|
|
if (cf->cf_fstate == FSTATE_NOTFOUND)
|
|
|
|
cf->cf_fstate = FSTATE_FOUND;
|
2000-01-18 10:45:04 +03:00
|
|
|
#ifdef __BROKEN_CONFIG_UNIT_USAGE
|
2002-09-26 08:07:35 +04:00
|
|
|
/*
|
|
|
|
* Bump the unit number on all starred cfdata
|
|
|
|
* entries for this device.
|
|
|
|
*/
|
2002-09-24 03:16:06 +04:00
|
|
|
if (cf->cf_fstate == FSTATE_STAR)
|
|
|
|
cf->cf_unit++;
|
2000-01-18 10:45:04 +03:00
|
|
|
#endif /* __BROKEN_CONFIG_UNIT_USAGE */
|
2002-09-24 03:16:06 +04:00
|
|
|
}
|
1996-12-05 03:09:10 +03:00
|
|
|
}
|
2002-09-24 03:16:06 +04:00
|
|
|
}
|
2000-02-01 07:01:19 +03:00
|
|
|
#ifdef __HAVE_DEVICE_REGISTER
|
1996-12-05 03:09:10 +03:00
|
|
|
device_register(dev, aux);
|
2006-02-18 22:09:53 +03:00
|
|
|
#endif
|
2007-12-09 23:27:42 +03:00
|
|
|
|
2006-02-18 22:09:53 +03:00
|
|
|
#if defined(SPLASHSCREEN) && defined(SPLASHSCREEN_PROGRESS)
|
|
|
|
if (splash_progress_state)
|
|
|
|
splash_progress_update(splash_progress_state);
|
1996-12-05 03:09:10 +03:00
|
|
|
#endif
|
2007-03-05 23:32:43 +03:00
|
|
|
(*dev->dv_cfattach->ca_attach)(parent, dev, aux);
|
2006-02-18 22:09:53 +03:00
|
|
|
#if defined(SPLASHSCREEN) && defined(SPLASHSCREEN_PROGRESS)
|
|
|
|
if (splash_progress_state)
|
|
|
|
splash_progress_update(splash_progress_state);
|
|
|
|
#endif
|
2007-12-09 23:27:42 +03:00
|
|
|
|
|
|
|
if (!device_pmf_is_registered(dev))
|
2007-12-10 00:11:57 +03:00
|
|
|
aprint_debug_dev(dev, "WARNING: power management not supported\n");
|
2007-12-09 23:27:42 +03:00
|
|
|
|
1999-09-15 23:37:08 +04:00
|
|
|
config_process_deferred(&deferred_config_queue, dev);
|
1996-12-05 03:09:10 +03:00
|
|
|
return (dev);
|
|
|
|
}
|
1993-08-13 17:19:41 +04:00
|
|
|
|
2005-12-20 07:39:36 +03:00
|
|
|
device_t
|
|
|
|
config_attach(device_t parent, cfdata_t cf, void *aux, cfprint_t print)
|
|
|
|
{
|
|
|
|
|
|
|
|
return (config_attach_loc(parent, cf, NULL, aux, print));
|
|
|
|
}
|
|
|
|
|
2002-10-09 06:59:55 +04:00
|
|
|
/*
|
|
|
|
* As above, but for pseudo-devices. Pseudo-devices attached in this
|
|
|
|
* way are silently inserted into the device tree, and their children
|
|
|
|
* attached.
|
|
|
|
*
|
|
|
|
* Note that because pseudo-devices are attached silently, any information
|
|
|
|
* the attach routine wishes to print should be prefixed with the device
|
|
|
|
* name by the attach routine.
|
|
|
|
*/
|
2005-12-20 07:39:36 +03:00
|
|
|
device_t
|
|
|
|
config_attach_pseudo(cfdata_t cf)
|
2002-10-09 06:59:55 +04:00
|
|
|
{
|
2005-12-20 07:39:36 +03:00
|
|
|
device_t dev;
|
2002-10-09 06:59:55 +04:00
|
|
|
|
2007-03-05 23:32:43 +03:00
|
|
|
dev = config_devalloc(ROOT, cf, NULL);
|
|
|
|
if (!dev)
|
2002-10-09 06:59:55 +04:00
|
|
|
return (NULL);
|
|
|
|
|
2007-03-05 23:32:43 +03:00
|
|
|
/* XXX mark busy in cfdata */
|
2002-10-09 06:59:55 +04:00
|
|
|
|
2007-03-05 23:32:43 +03:00
|
|
|
config_devlink(dev);
|
2002-10-09 06:59:55 +04:00
|
|
|
|
|
|
|
#if 0 /* XXXJRT not yet */
|
|
|
|
#ifdef __HAVE_DEVICE_REGISTER
|
|
|
|
device_register(dev, NULL); /* like a root node */
|
|
|
|
#endif
|
|
|
|
#endif
|
2007-03-05 23:32:43 +03:00
|
|
|
(*dev->dv_cfattach->ca_attach)(ROOT, dev, NULL);
|
2002-10-09 06:59:55 +04:00
|
|
|
config_process_deferred(&deferred_config_queue, dev);
|
|
|
|
return (dev);
|
|
|
|
}
|
|
|
|
|
1998-11-17 11:38:07 +03:00
|
|
|
/*
|
|
|
|
* Detach a device. Optionally forced (e.g. because of hardware
|
|
|
|
* removal) and quiet. Returns zero if successful, non-zero
|
|
|
|
* (an error code) otherwise.
|
|
|
|
*
|
|
|
|
* Note that this code wants to be run from a process context, so
|
|
|
|
* that the detach can sleep to allow processes which have a device
|
|
|
|
* open to run and unwind their stacks.
|
|
|
|
*/
|
|
|
|
int
|
2005-12-20 07:39:36 +03:00
|
|
|
config_detach(device_t dev, int flags)
|
1998-11-17 11:38:07 +03:00
|
|
|
{
|
2002-09-24 03:16:06 +04:00
|
|
|
struct cftable *ct;
|
2005-12-20 07:39:36 +03:00
|
|
|
cfdata_t cf;
|
2002-09-28 00:41:46 +04:00
|
|
|
const struct cfattach *ca;
|
1998-11-17 11:38:07 +03:00
|
|
|
struct cfdriver *cd;
|
|
|
|
#ifdef DIAGNOSTIC
|
2005-12-20 07:39:36 +03:00
|
|
|
device_t d;
|
1998-11-17 11:38:07 +03:00
|
|
|
#endif
|
2007-03-05 23:32:43 +03:00
|
|
|
int rv = 0;
|
1998-11-17 11:38:07 +03:00
|
|
|
|
|
|
|
#ifdef DIAGNOSTIC
|
2002-10-09 06:59:55 +04:00
|
|
|
if (dev->dv_cfdata != NULL &&
|
|
|
|
dev->dv_cfdata->cf_fstate != FSTATE_FOUND &&
|
|
|
|
dev->dv_cfdata->cf_fstate != FSTATE_STAR)
|
1998-11-17 11:38:07 +03:00
|
|
|
panic("config_detach: bad device fstate");
|
|
|
|
#endif
|
2002-10-09 06:59:55 +04:00
|
|
|
cd = dev->dv_cfdriver;
|
2002-09-27 06:24:06 +04:00
|
|
|
KASSERT(cd != NULL);
|
2002-10-04 05:50:53 +04:00
|
|
|
|
2002-10-09 06:59:55 +04:00
|
|
|
ca = dev->dv_cfattach;
|
2002-10-04 05:50:53 +04:00
|
|
|
KASSERT(ca != NULL);
|
1998-11-17 11:38:07 +03:00
|
|
|
|
|
|
|
/*
|
|
|
|
* Ensure the device is deactivated. If the device doesn't
|
|
|
|
* have an activation entry point, we allow DVF_ACTIVE to
|
|
|
|
* remain set. Otherwise, if DVF_ACTIVE is still set, the
|
|
|
|
* device is busy, and the detach fails.
|
|
|
|
*/
|
1998-11-18 21:40:08 +03:00
|
|
|
if (ca->ca_activate != NULL)
|
|
|
|
rv = config_deactivate(dev);
|
1998-11-17 11:38:07 +03:00
|
|
|
|
|
|
|
/*
|
|
|
|
* Try to detach the device. If that's not possible, then
|
|
|
|
* we either panic() (for the forced but failed case), or
|
|
|
|
* return an error.
|
|
|
|
*/
|
|
|
|
if (rv == 0) {
|
|
|
|
if (ca->ca_detach != NULL)
|
|
|
|
rv = (*ca->ca_detach)(dev, flags);
|
|
|
|
else
|
|
|
|
rv = EOPNOTSUPP;
|
|
|
|
}
|
|
|
|
if (rv != 0) {
|
|
|
|
if ((flags & DETACH_FORCE) == 0)
|
|
|
|
return (rv);
|
|
|
|
else
|
|
|
|
panic("config_detach: forced detach of %s failed (%d)",
|
|
|
|
dev->dv_xname, rv);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* The device has now been successfully detached.
|
|
|
|
*/
|
|
|
|
|
|
|
|
#ifdef DIAGNOSTIC
|
|
|
|
/*
|
|
|
|
* Sanity: If you're successfully detached, you should have no
|
|
|
|
* children. (Note that because children must be attached
|
|
|
|
* after parents, we only need to search the latter part of
|
|
|
|
* the list.)
|
|
|
|
*/
|
|
|
|
for (d = TAILQ_NEXT(dev, dv_list); d != NULL;
|
2000-01-25 16:23:26 +03:00
|
|
|
d = TAILQ_NEXT(d, dv_list)) {
|
|
|
|
if (d->dv_parent == dev) {
|
|
|
|
printf("config_detach: detached device %s"
|
|
|
|
" has children %s\n", dev->dv_xname, d->dv_xname);
|
|
|
|
panic("config_detach");
|
|
|
|
}
|
1998-11-17 11:38:07 +03:00
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
2004-08-18 02:13:18 +04:00
|
|
|
/* notify the parent that the child is gone */
|
|
|
|
if (dev->dv_parent) {
|
2005-12-20 07:39:36 +03:00
|
|
|
device_t p = dev->dv_parent;
|
2004-08-18 02:13:18 +04:00
|
|
|
if (p->dv_cfattach->ca_childdetached)
|
|
|
|
(*p->dv_cfattach->ca_childdetached)(p, dev);
|
|
|
|
}
|
|
|
|
|
1998-11-17 11:38:07 +03:00
|
|
|
/*
|
|
|
|
* Mark cfdata to show that the unit can be reused, if possible.
|
|
|
|
*/
|
2002-09-24 03:16:06 +04:00
|
|
|
TAILQ_FOREACH(ct, &allcftables, ct_list) {
|
2002-09-27 06:24:06 +04:00
|
|
|
for (cf = ct->ct_cfdata; cf->cf_name; cf++) {
|
|
|
|
if (STREQ(cf->cf_name, cd->cd_name)) {
|
2002-09-24 03:16:06 +04:00
|
|
|
if (cf->cf_fstate == FSTATE_FOUND &&
|
|
|
|
cf->cf_unit == dev->dv_unit)
|
|
|
|
cf->cf_fstate = FSTATE_NOTFOUND;
|
2000-01-18 10:45:04 +03:00
|
|
|
#ifdef __BROKEN_CONFIG_UNIT_USAGE
|
2002-09-26 08:07:35 +04:00
|
|
|
/*
|
|
|
|
* Note that we can only re-use a starred
|
|
|
|
* unit number if the unit being detached
|
|
|
|
* had the last assigned unit number.
|
|
|
|
*/
|
2002-09-24 03:16:06 +04:00
|
|
|
if (cf->cf_fstate == FSTATE_STAR &&
|
|
|
|
cf->cf_unit == dev->dv_unit + 1)
|
|
|
|
cf->cf_unit--;
|
2000-01-18 10:45:04 +03:00
|
|
|
#endif /* __BROKEN_CONFIG_UNIT_USAGE */
|
2002-09-24 03:16:06 +04:00
|
|
|
}
|
1998-11-17 11:38:07 +03:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2007-03-05 23:32:43 +03:00
|
|
|
config_devunlink(dev);
|
1998-11-17 11:38:07 +03:00
|
|
|
|
2002-10-09 06:59:55 +04:00
|
|
|
if (dev->dv_cfdata != NULL && (flags & DETACH_QUIET) == 0)
|
2003-01-01 02:59:11 +03:00
|
|
|
aprint_normal("%s detached\n", dev->dv_xname);
|
1998-11-17 11:38:07 +03:00
|
|
|
|
2007-03-05 23:32:43 +03:00
|
|
|
config_devdealloc(dev);
|
1998-11-17 11:38:07 +03:00
|
|
|
|
|
|
|
return (0);
|
|
|
|
}
|
|
|
|
|
2007-12-16 23:49:52 +03:00
|
|
|
struct config_detach_arg {
|
|
|
|
int a_flags;
|
|
|
|
int a_error;
|
|
|
|
};
|
|
|
|
|
|
|
|
static bool
|
|
|
|
config_detach_helper(device_t child, void *arg)
|
|
|
|
{
|
|
|
|
struct config_detach_arg *a = arg;
|
|
|
|
|
|
|
|
return (a->a_error = config_detach(child, a->a_flags)) == 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
int
|
|
|
|
config_detach_children(device_t parent, int flags)
|
|
|
|
{
|
|
|
|
struct config_detach_arg a = {.a_flags = flags, .a_error = 0};
|
|
|
|
|
|
|
|
device_foreach_child(parent, config_detach_helper, &a);
|
|
|
|
return a.a_error;
|
|
|
|
}
|
|
|
|
|
1998-11-17 11:38:07 +03:00
|
|
|
int
|
2005-12-20 07:39:36 +03:00
|
|
|
config_activate(device_t dev)
|
1998-11-17 11:38:07 +03:00
|
|
|
{
|
2002-10-04 05:50:53 +04:00
|
|
|
const struct cfattach *ca = dev->dv_cfattach;
|
1998-11-18 21:38:07 +03:00
|
|
|
int rv = 0, oflags = dev->dv_flags;
|
1998-11-17 11:38:07 +03:00
|
|
|
|
|
|
|
if (ca->ca_activate == NULL)
|
|
|
|
return (EOPNOTSUPP);
|
|
|
|
|
|
|
|
if ((dev->dv_flags & DVF_ACTIVE) == 0) {
|
|
|
|
dev->dv_flags |= DVF_ACTIVE;
|
|
|
|
rv = (*ca->ca_activate)(dev, DVACT_ACTIVATE);
|
1998-11-18 21:38:07 +03:00
|
|
|
if (rv)
|
|
|
|
dev->dv_flags = oflags;
|
1998-11-17 11:38:07 +03:00
|
|
|
}
|
|
|
|
return (rv);
|
|
|
|
}
|
|
|
|
|
|
|
|
int
|
2005-12-20 07:39:36 +03:00
|
|
|
config_deactivate(device_t dev)
|
1998-11-17 11:38:07 +03:00
|
|
|
{
|
2002-10-04 05:50:53 +04:00
|
|
|
const struct cfattach *ca = dev->dv_cfattach;
|
1998-11-18 21:38:07 +03:00
|
|
|
int rv = 0, oflags = dev->dv_flags;
|
1998-11-17 11:38:07 +03:00
|
|
|
|
|
|
|
if (ca->ca_activate == NULL)
|
|
|
|
return (EOPNOTSUPP);
|
|
|
|
|
|
|
|
if (dev->dv_flags & DVF_ACTIVE) {
|
|
|
|
dev->dv_flags &= ~DVF_ACTIVE;
|
|
|
|
rv = (*ca->ca_activate)(dev, DVACT_DEACTIVATE);
|
1998-11-18 21:38:07 +03:00
|
|
|
if (rv)
|
|
|
|
dev->dv_flags = oflags;
|
1998-11-17 11:38:07 +03:00
|
|
|
}
|
|
|
|
return (rv);
|
|
|
|
}
|
|
|
|
|
1998-06-09 22:46:12 +04:00
|
|
|
/*
|
|
|
|
* Defer the configuration of the specified device until all
|
|
|
|
* of its parent's devices have been attached.
|
|
|
|
*/
|
|
|
|
void
|
2005-12-20 07:39:36 +03:00
|
|
|
config_defer(device_t dev, void (*func)(device_t))
|
1998-06-09 22:46:12 +04:00
|
|
|
{
|
|
|
|
struct deferred_config *dc;
|
|
|
|
|
|
|
|
if (dev->dv_parent == NULL)
|
|
|
|
panic("config_defer: can't defer config of a root device");
|
|
|
|
|
|
|
|
#ifdef DIAGNOSTIC
|
|
|
|
for (dc = TAILQ_FIRST(&deferred_config_queue); dc != NULL;
|
|
|
|
dc = TAILQ_NEXT(dc, dc_queue)) {
|
|
|
|
if (dc->dc_dev == dev)
|
|
|
|
panic("config_defer: deferred twice");
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
1999-09-18 00:11:56 +04:00
|
|
|
dc = malloc(sizeof(*dc), M_DEVBUF, cold ? M_NOWAIT : M_WAITOK);
|
|
|
|
if (dc == NULL)
|
|
|
|
panic("config_defer: unable to allocate callback");
|
1998-06-09 22:46:12 +04:00
|
|
|
|
|
|
|
dc->dc_dev = dev;
|
|
|
|
dc->dc_func = func;
|
|
|
|
TAILQ_INSERT_TAIL(&deferred_config_queue, dc, dc_queue);
|
2000-01-24 21:03:19 +03:00
|
|
|
config_pending_incr();
|
1998-06-09 22:46:12 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
1999-09-15 23:37:08 +04:00
|
|
|
* Defer some autoconfiguration for a device until after interrupts
|
|
|
|
* are enabled.
|
|
|
|
*/
|
|
|
|
void
|
2005-12-20 07:39:36 +03:00
|
|
|
config_interrupts(device_t dev, void (*func)(device_t))
|
1999-09-15 23:37:08 +04:00
|
|
|
{
|
|
|
|
struct deferred_config *dc;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* If interrupts are enabled, callback now.
|
|
|
|
*/
|
1999-09-18 00:11:56 +04:00
|
|
|
if (cold == 0) {
|
1999-09-15 23:37:08 +04:00
|
|
|
(*func)(dev);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
#ifdef DIAGNOSTIC
|
|
|
|
for (dc = TAILQ_FIRST(&interrupt_config_queue); dc != NULL;
|
|
|
|
dc = TAILQ_NEXT(dc, dc_queue)) {
|
|
|
|
if (dc->dc_dev == dev)
|
|
|
|
panic("config_interrupts: deferred twice");
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
1999-09-18 00:11:56 +04:00
|
|
|
dc = malloc(sizeof(*dc), M_DEVBUF, cold ? M_NOWAIT : M_WAITOK);
|
|
|
|
if (dc == NULL)
|
|
|
|
panic("config_interrupts: unable to allocate callback");
|
1999-09-15 23:37:08 +04:00
|
|
|
|
|
|
|
dc->dc_dev = dev;
|
|
|
|
dc->dc_func = func;
|
|
|
|
TAILQ_INSERT_TAIL(&interrupt_config_queue, dc, dc_queue);
|
2000-01-24 21:03:19 +03:00
|
|
|
config_pending_incr();
|
1999-09-15 23:37:08 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Process a deferred configuration queue.
|
1998-06-09 22:46:12 +04:00
|
|
|
*/
|
|
|
|
static void
|
2000-06-02 05:31:52 +04:00
|
|
|
config_process_deferred(struct deferred_config_head *queue,
|
2005-12-20 07:39:36 +03:00
|
|
|
device_t parent)
|
1998-06-09 22:46:12 +04:00
|
|
|
{
|
|
|
|
struct deferred_config *dc, *ndc;
|
|
|
|
|
1999-09-15 23:37:08 +04:00
|
|
|
for (dc = TAILQ_FIRST(queue); dc != NULL; dc = ndc) {
|
1998-06-09 22:46:12 +04:00
|
|
|
ndc = TAILQ_NEXT(dc, dc_queue);
|
1999-09-15 23:37:08 +04:00
|
|
|
if (parent == NULL || dc->dc_dev->dv_parent == parent) {
|
|
|
|
TAILQ_REMOVE(queue, dc, dc_queue);
|
1998-06-09 22:46:12 +04:00
|
|
|
(*dc->dc_func)(dc->dc_dev);
|
|
|
|
free(dc, M_DEVBUF);
|
2000-01-24 21:03:19 +03:00
|
|
|
config_pending_decr();
|
1998-06-09 22:46:12 +04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2000-01-24 21:03:19 +03:00
|
|
|
/*
|
|
|
|
* Manipulate the config_pending semaphore.
|
|
|
|
*/
|
|
|
|
void
|
2000-06-02 05:31:52 +04:00
|
|
|
config_pending_incr(void)
|
2000-01-24 21:03:19 +03:00
|
|
|
{
|
|
|
|
|
|
|
|
config_pending++;
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
2000-06-02 05:31:52 +04:00
|
|
|
config_pending_decr(void)
|
2000-01-24 21:03:19 +03:00
|
|
|
{
|
|
|
|
|
|
|
|
#ifdef DIAGNOSTIC
|
|
|
|
if (config_pending == 0)
|
|
|
|
panic("config_pending_decr: config_pending == 0");
|
|
|
|
#endif
|
|
|
|
config_pending--;
|
|
|
|
if (config_pending == 0)
|
2005-05-30 02:24:14 +04:00
|
|
|
wakeup(&config_pending);
|
2000-01-24 21:03:19 +03:00
|
|
|
}
|
|
|
|
|
2002-10-01 22:11:57 +04:00
|
|
|
/*
|
|
|
|
* Register a "finalization" routine. Finalization routines are
|
|
|
|
* called iteratively once all real devices have been found during
|
|
|
|
* autoconfiguration, for as long as any one finalizer has done
|
|
|
|
* any work.
|
|
|
|
*/
|
|
|
|
int
|
2005-12-20 07:39:36 +03:00
|
|
|
config_finalize_register(device_t dev, int (*fn)(device_t))
|
2002-10-01 22:11:57 +04:00
|
|
|
{
|
|
|
|
struct finalize_hook *f;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* If finalization has already been done, invoke the
|
|
|
|
* callback function now.
|
|
|
|
*/
|
|
|
|
if (config_finalize_done) {
|
|
|
|
while ((*fn)(dev) != 0)
|
|
|
|
/* loop */ ;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Ensure this isn't already on the list. */
|
|
|
|
TAILQ_FOREACH(f, &config_finalize_list, f_list) {
|
|
|
|
if (f->f_func == fn && f->f_dev == dev)
|
|
|
|
return (EEXIST);
|
|
|
|
}
|
|
|
|
|
|
|
|
f = malloc(sizeof(*f), M_TEMP, M_WAITOK);
|
|
|
|
f->f_func = fn;
|
|
|
|
f->f_dev = dev;
|
|
|
|
TAILQ_INSERT_TAIL(&config_finalize_list, f, f_list);
|
|
|
|
|
|
|
|
return (0);
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
config_finalize(void)
|
|
|
|
{
|
|
|
|
struct finalize_hook *f;
|
|
|
|
int rv;
|
|
|
|
|
|
|
|
/* Run the hooks until none of them does any work. */
|
|
|
|
do {
|
|
|
|
rv = 0;
|
|
|
|
TAILQ_FOREACH(f, &config_finalize_list, f_list)
|
|
|
|
rv |= (*f->f_func)(f->f_dev);
|
|
|
|
} while (rv != 0);
|
|
|
|
|
|
|
|
config_finalize_done = 1;
|
|
|
|
|
|
|
|
/* Now free all the hooks. */
|
|
|
|
while ((f = TAILQ_FIRST(&config_finalize_list)) != NULL) {
|
|
|
|
TAILQ_REMOVE(&config_finalize_list, f, f_list);
|
|
|
|
free(f, M_TEMP);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2006-02-19 18:01:21 +03:00
|
|
|
/*
|
|
|
|
* device_lookup:
|
|
|
|
*
|
|
|
|
* Look up a device instance for a given driver.
|
|
|
|
*/
|
|
|
|
void *
|
|
|
|
device_lookup(cfdriver_t cd, int unit)
|
|
|
|
{
|
|
|
|
|
|
|
|
if (unit < 0 || unit >= cd->cd_ndevs)
|
|
|
|
return (NULL);
|
|
|
|
|
|
|
|
return (cd->cd_devs[unit]);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Accessor functions for the device_t type.
|
|
|
|
*/
|
|
|
|
devclass_t
|
|
|
|
device_class(device_t dev)
|
|
|
|
{
|
|
|
|
|
|
|
|
return (dev->dv_class);
|
|
|
|
}
|
|
|
|
|
|
|
|
cfdata_t
|
|
|
|
device_cfdata(device_t dev)
|
|
|
|
{
|
|
|
|
|
|
|
|
return (dev->dv_cfdata);
|
|
|
|
}
|
|
|
|
|
|
|
|
cfdriver_t
|
|
|
|
device_cfdriver(device_t dev)
|
|
|
|
{
|
|
|
|
|
|
|
|
return (dev->dv_cfdriver);
|
|
|
|
}
|
|
|
|
|
|
|
|
cfattach_t
|
|
|
|
device_cfattach(device_t dev)
|
|
|
|
{
|
|
|
|
|
|
|
|
return (dev->dv_cfattach);
|
|
|
|
}
|
|
|
|
|
|
|
|
int
|
|
|
|
device_unit(device_t dev)
|
|
|
|
{
|
|
|
|
|
|
|
|
return (dev->dv_unit);
|
|
|
|
}
|
|
|
|
|
|
|
|
const char *
|
|
|
|
device_xname(device_t dev)
|
|
|
|
{
|
|
|
|
|
|
|
|
return (dev->dv_xname);
|
|
|
|
}
|
|
|
|
|
|
|
|
device_t
|
|
|
|
device_parent(device_t dev)
|
|
|
|
{
|
|
|
|
|
|
|
|
return (dev->dv_parent);
|
|
|
|
}
|
|
|
|
|
2007-12-16 23:49:52 +03:00
|
|
|
bool
|
|
|
|
device_foreach_child(device_t parent, bool (*func)(device_t, void *), void *arg)
|
|
|
|
{
|
2008-01-08 09:27:46 +03:00
|
|
|
device_t curdev, nextdev;
|
2007-12-16 23:49:52 +03:00
|
|
|
|
2008-01-08 09:27:46 +03:00
|
|
|
for (curdev = TAILQ_FIRST(&alldevs); curdev != NULL; curdev = nextdev) {
|
|
|
|
nextdev = TAILQ_NEXT(curdev, dv_list);
|
2007-12-16 23:49:52 +03:00
|
|
|
if (device_parent(curdev) != parent)
|
|
|
|
continue;
|
|
|
|
if (!(*func)(curdev, arg))
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2007-02-22 01:59:35 +03:00
|
|
|
bool
|
2006-02-19 18:01:21 +03:00
|
|
|
device_is_active(device_t dev)
|
|
|
|
{
|
2007-12-09 23:27:42 +03:00
|
|
|
int active_flags;
|
|
|
|
|
|
|
|
active_flags = DVF_ACTIVE;
|
|
|
|
active_flags |= DVF_CLASS_SUSPENDED;
|
|
|
|
active_flags |= DVF_DRIVER_SUSPENDED;
|
|
|
|
active_flags |= DVF_BUS_SUSPENDED;
|
|
|
|
|
|
|
|
return ((dev->dv_flags & active_flags) == DVF_ACTIVE);
|
|
|
|
}
|
|
|
|
|
|
|
|
bool
|
|
|
|
device_is_enabled(device_t dev)
|
|
|
|
{
|
|
|
|
return (dev->dv_flags & DVF_ACTIVE) == DVF_ACTIVE;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool
|
|
|
|
device_has_power(device_t dev)
|
|
|
|
{
|
|
|
|
int active_flags;
|
2006-02-19 18:01:21 +03:00
|
|
|
|
2007-12-09 23:27:42 +03:00
|
|
|
active_flags = DVF_ACTIVE | DVF_BUS_SUSPENDED;
|
|
|
|
|
|
|
|
return ((dev->dv_flags & active_flags) == DVF_ACTIVE);
|
2006-02-19 18:01:21 +03:00
|
|
|
}
|
|
|
|
|
2006-03-29 10:00:46 +04:00
|
|
|
int
|
2006-03-29 10:25:35 +04:00
|
|
|
device_locator(device_t dev, u_int locnum)
|
2006-02-19 18:01:21 +03:00
|
|
|
{
|
|
|
|
|
2006-03-29 10:00:46 +04:00
|
|
|
KASSERT(dev->dv_locators != NULL);
|
|
|
|
return (dev->dv_locators[locnum]);
|
2006-02-19 18:01:21 +03:00
|
|
|
}
|
2006-02-23 08:48:12 +03:00
|
|
|
|
2006-03-29 10:08:16 +04:00
|
|
|
void *
|
|
|
|
device_private(device_t dev)
|
|
|
|
{
|
|
|
|
|
2007-09-24 22:47:56 +04:00
|
|
|
return (dev->dv_private);
|
2006-03-29 10:08:16 +04:00
|
|
|
}
|
|
|
|
|
2006-05-05 22:04:41 +04:00
|
|
|
prop_dictionary_t
|
|
|
|
device_properties(device_t dev)
|
|
|
|
{
|
|
|
|
|
|
|
|
return (dev->dv_properties);
|
|
|
|
}
|
|
|
|
|
2006-02-23 08:48:12 +03:00
|
|
|
/*
|
|
|
|
* device_is_a:
|
|
|
|
*
|
|
|
|
* Returns true if the device is an instance of the specified
|
|
|
|
* driver.
|
|
|
|
*/
|
2007-02-22 01:59:35 +03:00
|
|
|
bool
|
2006-02-23 08:48:12 +03:00
|
|
|
device_is_a(device_t dev, const char *dname)
|
|
|
|
{
|
|
|
|
|
|
|
|
return (strcmp(dev->dv_cfdriver->cd_name, dname) == 0);
|
|
|
|
}
|
2007-12-09 23:27:42 +03:00
|
|
|
|
|
|
|
/*
|
|
|
|
* Power management related functions.
|
|
|
|
*/
|
|
|
|
|
|
|
|
bool
|
|
|
|
device_pmf_is_registered(device_t dev)
|
|
|
|
{
|
|
|
|
return (dev->dv_flags & DVF_POWER_HANDLERS) != 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool
|
|
|
|
device_pmf_driver_suspend(device_t dev)
|
|
|
|
{
|
|
|
|
if ((dev->dv_flags & DVF_DRIVER_SUSPENDED) != 0)
|
|
|
|
return true;
|
|
|
|
if ((dev->dv_flags & DVF_CLASS_SUSPENDED) == 0)
|
|
|
|
return false;
|
|
|
|
if (*dev->dv_driver_suspend != NULL &&
|
|
|
|
!(*dev->dv_driver_suspend)(dev))
|
|
|
|
return false;
|
|
|
|
|
|
|
|
dev->dv_flags |= DVF_DRIVER_SUSPENDED;
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool
|
|
|
|
device_pmf_driver_resume(device_t dev)
|
|
|
|
{
|
|
|
|
if ((dev->dv_flags & DVF_DRIVER_SUSPENDED) == 0)
|
|
|
|
return true;
|
|
|
|
if ((dev->dv_flags & DVF_BUS_SUSPENDED) != 0)
|
|
|
|
return false;
|
|
|
|
if (*dev->dv_driver_resume != NULL &&
|
|
|
|
!(*dev->dv_driver_resume)(dev))
|
|
|
|
return false;
|
|
|
|
|
|
|
|
dev->dv_flags &= ~DVF_DRIVER_SUSPENDED;
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
device_pmf_driver_register(device_t dev,
|
|
|
|
bool (*suspend)(device_t), bool (*resume)(device_t))
|
|
|
|
{
|
|
|
|
dev->dv_driver_suspend = suspend;
|
|
|
|
dev->dv_driver_resume = resume;
|
|
|
|
dev->dv_flags |= DVF_POWER_HANDLERS;
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
device_pmf_driver_deregister(device_t dev)
|
|
|
|
{
|
|
|
|
dev->dv_driver_suspend = NULL;
|
|
|
|
dev->dv_driver_resume = NULL;
|
|
|
|
dev->dv_flags &= ~DVF_POWER_HANDLERS;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool
|
|
|
|
device_pmf_driver_child_register(device_t dev)
|
|
|
|
{
|
|
|
|
device_t parent = device_parent(dev);
|
|
|
|
|
|
|
|
if (parent == NULL || parent->dv_driver_child_register == NULL)
|
|
|
|
return true;
|
|
|
|
return (*parent->dv_driver_child_register)(dev);
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
device_pmf_driver_set_child_register(device_t dev,
|
|
|
|
bool (*child_register)(device_t))
|
|
|
|
{
|
|
|
|
dev->dv_driver_child_register = child_register;
|
|
|
|
}
|
|
|
|
|
|
|
|
void *
|
|
|
|
device_pmf_bus_private(device_t dev)
|
|
|
|
{
|
|
|
|
return dev->dv_bus_private;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool
|
|
|
|
device_pmf_bus_suspend(device_t dev)
|
|
|
|
{
|
|
|
|
if ((dev->dv_flags & DVF_BUS_SUSPENDED) != 0)
|
|
|
|
return true;
|
|
|
|
if ((dev->dv_flags & DVF_CLASS_SUSPENDED) == 0 ||
|
|
|
|
(dev->dv_flags & DVF_DRIVER_SUSPENDED) == 0)
|
|
|
|
return false;
|
|
|
|
if (*dev->dv_bus_suspend != NULL &&
|
|
|
|
!(*dev->dv_bus_suspend)(dev))
|
|
|
|
return false;
|
|
|
|
|
|
|
|
dev->dv_flags |= DVF_BUS_SUSPENDED;
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool
|
|
|
|
device_pmf_bus_resume(device_t dev)
|
|
|
|
{
|
|
|
|
if ((dev->dv_flags & DVF_BUS_SUSPENDED) == 0)
|
|
|
|
return true;
|
|
|
|
if (*dev->dv_bus_resume != NULL &&
|
|
|
|
!(*dev->dv_bus_resume)(dev))
|
|
|
|
return false;
|
|
|
|
|
|
|
|
dev->dv_flags &= ~DVF_BUS_SUSPENDED;
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
device_pmf_bus_register(device_t dev, void *priv,
|
|
|
|
bool (*suspend)(device_t), bool (*resume)(device_t),
|
|
|
|
void (*deregister)(device_t))
|
|
|
|
{
|
|
|
|
dev->dv_bus_private = priv;
|
|
|
|
dev->dv_bus_resume = resume;
|
|
|
|
dev->dv_bus_suspend = suspend;
|
|
|
|
dev->dv_bus_deregister = deregister;
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
device_pmf_bus_deregister(device_t dev)
|
|
|
|
{
|
|
|
|
if (dev->dv_bus_deregister == NULL)
|
|
|
|
return;
|
|
|
|
(*dev->dv_bus_deregister)(dev);
|
|
|
|
dev->dv_bus_private = NULL;
|
|
|
|
dev->dv_bus_suspend = NULL;
|
|
|
|
dev->dv_bus_resume = NULL;
|
|
|
|
dev->dv_bus_deregister = NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
void *
|
|
|
|
device_pmf_class_private(device_t dev)
|
|
|
|
{
|
|
|
|
return dev->dv_class_private;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool
|
|
|
|
device_pmf_class_suspend(device_t dev)
|
|
|
|
{
|
|
|
|
if ((dev->dv_flags & DVF_CLASS_SUSPENDED) != 0)
|
|
|
|
return true;
|
|
|
|
if (*dev->dv_class_suspend != NULL &&
|
|
|
|
!(*dev->dv_class_suspend)(dev))
|
|
|
|
return false;
|
|
|
|
|
|
|
|
dev->dv_flags |= DVF_CLASS_SUSPENDED;
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool
|
|
|
|
device_pmf_class_resume(device_t dev)
|
|
|
|
{
|
|
|
|
if ((dev->dv_flags & DVF_CLASS_SUSPENDED) == 0)
|
|
|
|
return true;
|
|
|
|
if ((dev->dv_flags & DVF_BUS_SUSPENDED) != 0 ||
|
|
|
|
(dev->dv_flags & DVF_DRIVER_SUSPENDED) != 0)
|
|
|
|
return false;
|
|
|
|
if (*dev->dv_class_resume != NULL &&
|
|
|
|
!(*dev->dv_class_resume)(dev))
|
|
|
|
return false;
|
|
|
|
|
|
|
|
dev->dv_flags &= ~DVF_CLASS_SUSPENDED;
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
device_pmf_class_register(device_t dev, void *priv,
|
|
|
|
bool (*suspend)(device_t), bool (*resume)(device_t),
|
|
|
|
void (*deregister)(device_t))
|
|
|
|
{
|
|
|
|
dev->dv_class_private = priv;
|
|
|
|
dev->dv_class_suspend = suspend;
|
|
|
|
dev->dv_class_resume = resume;
|
|
|
|
dev->dv_class_deregister = deregister;
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
device_pmf_class_deregister(device_t dev)
|
|
|
|
{
|
|
|
|
if (dev->dv_class_deregister == NULL)
|
|
|
|
return;
|
|
|
|
(*dev->dv_class_deregister)(dev);
|
|
|
|
dev->dv_class_private = NULL;
|
|
|
|
dev->dv_class_suspend = NULL;
|
|
|
|
dev->dv_class_resume = NULL;
|
|
|
|
dev->dv_class_deregister = NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool
|
|
|
|
device_active(device_t dev, devactive_t type)
|
|
|
|
{
|
|
|
|
size_t i;
|
|
|
|
|
|
|
|
if (dev->dv_activity_count == 0)
|
|
|
|
return false;
|
|
|
|
|
|
|
|
for (i = 0; i < dev->dv_activity_count; ++i)
|
|
|
|
(*dev->dv_activity_handlers[i])(dev, type);
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool
|
|
|
|
device_active_register(device_t dev, void (*handler)(device_t, devactive_t))
|
|
|
|
{
|
|
|
|
void (**new_handlers)(device_t, devactive_t);
|
|
|
|
void (**old_handlers)(device_t, devactive_t);
|
|
|
|
size_t i, new_size;
|
|
|
|
int s;
|
|
|
|
|
|
|
|
old_handlers = dev->dv_activity_handlers;
|
|
|
|
|
|
|
|
for (i = 0; i < dev->dv_activity_count; ++i) {
|
|
|
|
if (old_handlers[i] == handler)
|
|
|
|
panic("Double registering of idle handlers");
|
|
|
|
}
|
|
|
|
|
|
|
|
new_size = dev->dv_activity_count + 1;
|
|
|
|
new_handlers = malloc(sizeof(void *) * new_size, M_DEVBUF, M_WAITOK);
|
|
|
|
|
|
|
|
memcpy(new_handlers, old_handlers,
|
|
|
|
sizeof(void *) * dev->dv_activity_count);
|
|
|
|
new_handlers[new_size - 1] = handler;
|
|
|
|
|
|
|
|
s = splhigh();
|
|
|
|
dev->dv_activity_count = new_size;
|
|
|
|
dev->dv_activity_handlers = new_handlers;
|
|
|
|
splx(s);
|
|
|
|
|
|
|
|
if (old_handlers != NULL)
|
|
|
|
free(old_handlers, M_DEVBUF);
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
device_active_deregister(device_t dev, void (*handler)(device_t, devactive_t))
|
|
|
|
{
|
|
|
|
void (**new_handlers)(device_t, devactive_t);
|
|
|
|
void (**old_handlers)(device_t, devactive_t);
|
|
|
|
size_t i, new_size;
|
|
|
|
int s;
|
|
|
|
|
|
|
|
old_handlers = dev->dv_activity_handlers;
|
|
|
|
|
|
|
|
for (i = 0; i < dev->dv_activity_count; ++i) {
|
|
|
|
if (old_handlers[i] == handler)
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (i == dev->dv_activity_count)
|
|
|
|
return; /* XXX panic? */
|
|
|
|
|
|
|
|
new_size = dev->dv_activity_count - 1;
|
|
|
|
|
|
|
|
if (new_size == 0) {
|
|
|
|
new_handlers = NULL;
|
|
|
|
} else {
|
|
|
|
new_handlers = malloc(sizeof(void *) * new_size, M_DEVBUF,
|
|
|
|
M_WAITOK);
|
|
|
|
memcpy(new_handlers, old_handlers, sizeof(void *) * i);
|
|
|
|
memcpy(new_handlers + i, old_handlers + i + 1,
|
|
|
|
sizeof(void *) * (new_size - i));
|
|
|
|
}
|
|
|
|
|
|
|
|
s = splhigh();
|
|
|
|
dev->dv_activity_count = new_size;
|
|
|
|
dev->dv_activity_handlers = new_handlers;
|
|
|
|
splx(s);
|
|
|
|
|
|
|
|
free(old_handlers, M_DEVBUF);
|
|
|
|
}
|