modsubr.c revision 7730e2acd408d8c08610e39353747d95fe743bce
/*
* CDDL HEADER START
*
* The contents of this file are subject to the terms of the
* Common Development and Distribution License (the "License").
* You may not use this file except in compliance with the License.
*
* You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
* See the License for the specific language governing permissions
* and limitations under the License.
*
* When distributing Covered Code, include this CDDL HEADER in each
* file and include the License file at usr/src/OPENSOLARIS.LICENSE.
* If applicable, add the following below this CDDL HEADER, with the
* fields enclosed by brackets "[]" replaced with your own identifying
* information: Portions Copyright [yyyy] [name of copyright owner]
*
* CDDL HEADER END
*/
/*
* Copyright 2008 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
#pragma ident "%Z%%M% %I% %E% SMI"
#include <sys/sysmacros.h>
#include <sys/pathname.h>
#include <sys/ddi_impldefs.h>
#include <sys/ddi_implfuncs.h>
#include <sys/systeminfo.h>
#include <sys/autoconf.h>
extern struct dev_ops mod_nodev_ops;
struct mod_noload {
struct mod_noload *mn_next;
char *mn_name;
};
/*
* Function prototypes
*/
static int nm_hash(char *);
static void make_syscallname(char *, int);
static void hwc_hash_init();
static void hwc_unhash(struct hwc_spec *);
struct dev_ops *
{
int loaded;
char *drvname;
return (NULL);
loaded = 1;
return (NULL);
}
if (loaded) {
} else {
}
return (ops);
}
#ifdef DEBUG_RELE
static int mod_rele_pause = DEBUG_RELE;
#endif /* DEBUG_RELE */
void
{
return;
#ifdef DEBUG_RELE
if (!DEV_OPS_HELD(ops)) {
char *s;
static char *msg = "mod_rele_dev_by_major: unheld driver!";
printf("mod_rele_dev_by_major: Major dev <%u>, name <%s>\n",
if (mod_rele_pause)
else
return; /* XXX: Note changed behavior */
}
#endif /* DEBUG_RELE */
if (!DEV_OPS_HELD(ops)) {
"mod_rele_dev_by_major: Unheld driver: major number <%u>",
}
}
struct dev_ops *
{
char *name;
return (NULL);
return (mod_hold_dev_by_major(major));
}
void
{
char *name;
return;
}
int
{
return (0);
}
int
{
return (-1);
}
int
{
return (EINVAL);
}
void
{
/* nothing */
}
/*
* Install all the stubs for a module.
* Return zero if there were no errors or an errno value.
*/
int
{
char *p;
char *filenamep;
struct mod_modinfo *mp;
p = name;
while (*p)
if (*p++ == '/')
filenamep = p;
/*
* Concatenate "name" with "_modname" then look up this symbol
* in the kernel. If not found, we're done.
* If found, then find the "mod" info structure and call init_stubs().
*/
p = namebuf;
*p++ = *filenamep++;
(void) strcpy(p, "_modinfo");
else
return (0);
}
static int
{
struct mod_stub_info *sp;
int i;
char *funcname;
/*
* Fill in all stubs for this module. We can't be lazy, since
* some calls could come in from interrupt level, and we
* can't modlookup then (symbols may be paged out).
*/
printf("init_stubs: couldn't find symbol "
return (EFAULT);
}
printf("%s:%s() not defined properly\n",
return (EFAULT);
}
}
return (0);
}
/*
* modp->mod_modinfo has to be checked in these functions before
* mod_stub_info is accessed because it's not guranteed that all
* modules define mod_stub_info structures.
*/
void
{
struct mod_stub_info *stub;
if (modp->mod_modinfo) {
}
}
}
void
{
struct mod_stub_info *stub;
if (modp->mod_modinfo) {
}
}
}
void
{
struct mod_stub_info *stub;
if (modp->mod_modinfo) {
else
}
}
}
struct modctl *
{
do {
return (modp);
}
return (NULL);
}
/*
* Attach driver.conf info to devnames for a driver
*/
struct par_list *
{
int err;
/*
* If .conf file already parsed or driver removed, just return.
* May return NULL.
*/
return (NULL);
if (err) /* file doesn't exist */
return (NULL);
/*
* If there are global properties, reference it from dnp.
*/
if (props)
/*
* Hash specs to be looked up by nexus drivers
*/
while (tmp) {
}
if (!i_ddi_io_initialized()) {
}
return (pl);
}
/*
* Destroy driver.conf info in devnames array for a driver
*/
int
{
/*
* Unref driver global property list. Don't destroy it
* because some instances may still be referencing it.
* The property list will be freed when the last ref
* goes away.
*/
if (dnp->dn_global_prop_ptr) {
}
/*
* remove specs from hash table
*/
return (0);
}
static int
{
char c;
int hash = 0;
hash ^= c;
return (hash & MOD_BIND_HASHMASK);
}
void
{
int i;
for (i = 0; i < MOD_BIND_HASHSIZE; i++) {
if (bp->b_bind_name) {
}
}
}
}
static struct bind *
{
int hashndx;
break;
}
return (mb);
}
/*
* Create an entry for the given (name, major, bind_name) tuple in the
* hash table supplied. Reject the attempt to do so if 'name' is already
* in the hash table.
*
* Does not provide synchronization, so use only during boot or with
* externally provided locking.
*/
int
{
int hashndx;
/*
* Fail if the key being added is already in the hash table
*/
return (-1);
}
return (0);
}
/*
* Delete a binding from a binding-hash.
*
* Does not provide synchronization, so use only during boot or with
* externally provided locking.
*/
void
{
int hashndx;
return;
t = b;
} else {
t = b;
break;
}
bparent = b;
}
}
if (t != NULL) { /* delete the target */
if (t->b_bind_name)
}
}
mod_name_to_major(char *name)
{
return ((major_t)-1);
}
char *
{
return (NULL);
}
/*
* Set up the devnames array. Error check for duplicate entries.
*/
void
init_devnamesp(int size)
{
int hshndx;
static char dupwarn[] =
"!Device entry \"%s %d\" conflicts with previous entry \"%s %d\" "
"in /etc/name_to_major.";
static char badmaj[] = "The major number %u is invalid.";
/*
* Allocate the devnames array. All mutexes and cv's will be
* automagically initialized.
*/
/*
* Stick the contents of mb_hashtab into the devnames array. Warn if
* two hash entries correspond to the same major number, or if a
* major number is out of range.
*/
/*
* If there is not an entry at b_num already,
* then this must be a bad major number.
*/
} else {
}
}
}
}
/* Initialize hash table for hwc_spec's */
}
int
{
char *copy;
/*
* Until on-disk support for major nums > 14 bits arrives, fail
* any major numbers that are too big.
*/
if (major > L_MAXMAJ32)
return (EINVAL);
/* Another driver already here */
return (EINVAL);
}
/* Adding back a removed driver */
return (0);
}
/*
* Check if flag is taken by getudev()
*/
return (EINVAL);
}
/* Make sure string is copied before setting dn_name */
return (0);
}
/*
* Set up the syscallnames array.
*/
void
init_syscallnames(int size)
{
int hshndx;
"!Couldn't add system call \"%s %d\". "
"Value out of range (0..%d) in "
"/etc/name_to_sysnum.",
continue;
}
}
}
}
static void
{
"It conflicts with \"%s %d\" in /etc/name_to_sysnum.",
return;
}
}
/*
* Given a system call name, get its number.
*/
int
mod_getsysnum(char *name)
{
return (-1);
}
/*
* Given a system call number, get the system call name.
*/
char *
mod_getsysname(int sysnum)
{
return (syscallnames[sysnum]);
}
/*
* Find the name of the module containing the specified pc.
* Returns the name on success, "<unknown>" on failure.
* No mod_lock locking is required because things are never deleted from
* the &modules list.
*/
char *
{
do {
return (mcp->mod_modname);
return ("<unknown>");
}
/*
* Hash tables for hwc_spec
*
* The purpose of these hash tables are to allow the framework to discover
* all possible .conf children for a given nexus. There are two hash tables.
* One is hashed based on parent name, the on the class name. Each
* driver.conf file translates to a list of hwc_spec's. Adding and
* removing the entire list is an atomic operation, protected by
* the hwc_hash_lock.
*
* What we get from all the hashing is the function hwc_get_child_spec().
*/
/*
* Initialize hash tables for parent and class specs
*/
static void
{
}
/*
* Insert a spec into hash table. hwc_hash_lock must be held
*/
static void
{
(mod_hash_val_t)&entry) != 0) {
/* Name doesn't exist, insert a new key */
}
return;
}
/*
* Name is already present, append spec to the list.
* This is the case when driver.conf specifies multiple
* nodes under a single parent or class.
*/
while (entry->hwc_hash_next)
}
/*
* Remove a spec entry from spec hash table, the spec itself is
* destroyed external to this function.
*/
static void
{
char *key;
(mod_hash_val_t)&entry) != 0) {
return; /* name not found in hash */
}
/*
* If the head is the spec to be removed, either destroy the
* entry or replace it with the remaining list.
*/
return;
}
return;
}
/*
* If the head is not the one, look for the spec in the
* hwc_hash_next linkage.
*/
if (entry->hwc_hash_next) {
}
}
/*
* Hash a list of specs based on either parent name or class name
*/
static void
{
while (spec) {
/* Put driver major here so parent can find it */
} else {
"hwc_hash: No class or parent specified");
}
}
}
/*
* Remove a list of specs from hash tables. Don't destroy the specs yet.
*/
static void
{
while (spec) {
} else {
"hwc_unhash: No class or parent specified");
}
}
}
/*
* Make a copy of specs in a hash entry and add to the end of listp.
* Called by nexus to locate a list of child specs.
*
* entry is a list of hwc_spec chained together with hwc_hash_next.
* listp points to list chained together with hwc_next.
*/
static void
{
/* Find the tail of the list */
while (*listp)
while (entry) {
continue;
}
/*
* Allocate spec and copy the content of entry.
* already knows the parent dip.
*/
}
}
/*
* Given a dip, find the list of child .conf specs from most specific
* (parent pathname) to least specific (class name).
*
* This function allows top-down loading to be implemented without
* changing the format of driver.conf file.
*/
struct hwc_spec *
{
extern char *i_ddi_parname(dev_info_t *, char *);
extern int i_ddi_get_exported_classes(dev_info_t *, char ***);
extern void i_ddi_free_exported_classes(char **, int);
int i, nclass;
char **classes;
char *parname, *parname_buf;
char *deviname, *deviname_buf;
char *pathname, *pathname_buf;
char *bindname;
char *drvname;
/*
* Lookup based on full path.
* In the case of root node, ddi_pathname would return
* null string so just skip calling it.
* As the pathname always begins with /, no simpler
* name can duplicate it.
*/
}
/*
* Lookup nodename@address.
* Note deviname cannot match pathname.
*/
if (*deviname != '\0') {
/*
* Skip leading / returned by ddi_deviname.
*/
deviname++;
}
/*
* Lookup bindingname@address.
* Take care not to perform duplicate lookups.
*/
if (*parname != '\0') {
}
}
/*
* Lookup driver binding name
*/
/*
* Lookup driver name
*/
/*
* Lookup classes exported by this node and lookup the
* class hash table for all .conf specs
*/
for (i = 0; i < nclass; i++) {
&val) == 0)
}
return (list);
}