1354 lines
28 KiB
C
1354 lines
28 KiB
C
/* @(#)vdconf.c 1.1 94/10/31 SMI */
|
|
|
|
/*
|
|
* Copyright (c) 1988-1990 by Sun Microsystems, Inc.
|
|
*/
|
|
|
|
#ifdef VDDRV
|
|
|
|
#include <sys/syscall.h>
|
|
#include <sys/param.h>
|
|
#include <sys/user.h>
|
|
#include <sys/mbuf.h>
|
|
#include <sys/buf.h>
|
|
#include <sys/file.h>
|
|
#include <sys/map.h>
|
|
#include <sys/vm.h>
|
|
#include <sys/conf.h>
|
|
#include <sys/socket.h>
|
|
#include <sys/vfs.h>
|
|
#include <sys/mount.h>
|
|
#include <sys/ioctl.h>
|
|
#include <sys/systm.h>
|
|
#include <machine/pte.h>
|
|
#include <machine/psl.h>
|
|
|
|
#include <sundev/mbvar.h>
|
|
|
|
#include <net/if.h>
|
|
#include <net/af.h>
|
|
#include <net/route.h>
|
|
#include <netinet/in.h>
|
|
#include <netinet/in_pcb.h>
|
|
#include <netinet/in_var.h>
|
|
|
|
#include <sun/autoconf.h>
|
|
#include <sun/vddrv.h>
|
|
|
|
/*
|
|
* Define vd_moduleops for each supported module type
|
|
*/
|
|
|
|
/*
|
|
* Null operations; used for uninitialized and "user" modules.
|
|
*/
|
|
int vd_null();
|
|
|
|
struct vd_moduleops vd_nullops = {
|
|
vd_null, vd_null, vd_null
|
|
};
|
|
|
|
/*
|
|
* Device drivers
|
|
*/
|
|
int vd_statdrv(), vd_installdrv(), vd_removedrv();
|
|
|
|
struct vd_moduleops vd_driverops = {
|
|
vd_statdrv, vd_installdrv, vd_removedrv
|
|
};
|
|
|
|
/*
|
|
* Network interface drivers
|
|
*/
|
|
int vd_installnet(), vd_removenet();
|
|
|
|
struct vd_moduleops vd_netops = {
|
|
vd_null, vd_installnet, vd_removenet
|
|
};
|
|
|
|
/*
|
|
* System calls
|
|
*/
|
|
|
|
int vd_statsys(), vd_installsys(), vd_removesys();
|
|
|
|
struct vd_moduleops vd_syscallops = {
|
|
vd_statsys, vd_installsys, vd_removesys
|
|
};
|
|
|
|
extern int nosys(), errsys(), nodev(), nulldev(), seltrue();
|
|
extern char *strcpy();
|
|
#ifndef sun4c
|
|
extern int vd_checkirq();
|
|
extern void vd_resetirq();
|
|
#endif sun4c
|
|
|
|
struct bdevsw *vd_getbdevsw();
|
|
struct cdevsw *vd_getcdevsw();
|
|
struct sysent *vd_getsysent();
|
|
struct vdconf *vd_getvdconf();
|
|
int vd_linkdrv(), vd_configdrv(), vd_unconfigdrv();
|
|
int vd_checkdrvlinkage(), vd_unlinknet();
|
|
int vd_userconfig();
|
|
void vd_userunconfig();
|
|
#ifndef OPENPROMS
|
|
int vd_confctlrs(), vd_unconfctlrs();
|
|
int vd_confdevs(), vd_unconfdevs();
|
|
#endif
|
|
#ifdef UFS
|
|
int has_ufsmounted();
|
|
#endif
|
|
#if !defined(sun386) && !defined(OPENPROMS)
|
|
struct vec *allocvec();
|
|
void freevec();
|
|
#endif
|
|
|
|
/*
|
|
* Null operation; return 0.
|
|
*/
|
|
/*ARGSUSED*/
|
|
/*VARARGS1*/
|
|
int
|
|
vd_null(vdp, vdi)
|
|
struct vddrv *vdp;
|
|
caddr_t vdi;
|
|
{
|
|
return (0);
|
|
}
|
|
|
|
/*
|
|
* Driver status info
|
|
*/
|
|
int
|
|
vd_statdrv(vdp, vds)
|
|
struct vddrv *vdp;
|
|
struct vdstat *vds;
|
|
{
|
|
vds->vds_modinfo[0] = vdp->vdd_vdtab->vdldrv_blockmajor;
|
|
vds->vds_modinfo[1] = vdp->vdd_vdtab->vdldrv_charmajor;
|
|
return (0);
|
|
}
|
|
|
|
/*
|
|
* Install a new driver
|
|
*/
|
|
|
|
int
|
|
vd_installdrv(vdp, vdi)
|
|
struct vddrv *vdp;
|
|
struct vdioctl_load *vdi;
|
|
{
|
|
register struct vdlinkage *vdl = vdp->vdd_vdtab;
|
|
register int status;
|
|
|
|
/*
|
|
* Get user config info
|
|
*/
|
|
if (vdi->vdi_userconf != NULL) {
|
|
status = vd_userconfig(vdp, vdi);
|
|
if (status != 0)
|
|
return (status);
|
|
}
|
|
#ifdef OPENPROMS
|
|
status = vd_configdrv(vdp, vdl->vdldrv_dev_ops);
|
|
#else
|
|
status = vd_configdrv(vdp, vdl->vdldrv_mb_ctlr,
|
|
vdl->vdldrv_mb_driver, vdl->vdldrv_mb_device,
|
|
vdl->vdldrv_numctlrs, vdl->vdldrv_numdevs);
|
|
#endif OPENPROMS
|
|
if (status != 0)
|
|
return (status);
|
|
status = vd_linkdrv(vdp);
|
|
if (status != 0) {
|
|
#ifdef OPENPROMS
|
|
(void) vd_unconfigdrv(vdp, vdl->vdldrv_dev_ops);
|
|
#else
|
|
(void) vd_unconfigdrv(vdp, vdl->vdldrv_mb_ctlr,
|
|
vdl->vdldrv_mb_driver, vdl->vdldrv_mb_device,
|
|
vdl->vdldrv_numctlrs, vdl->vdldrv_numdevs);
|
|
#endif OPENPROMS
|
|
}
|
|
return (status);
|
|
}
|
|
|
|
int
|
|
vd_removedrv(vdp, vdi)
|
|
struct vddrv *vdp;
|
|
struct vdioctl_unload *vdi;
|
|
{
|
|
register struct vdlinkage *vdl = vdp->vdd_vdtab;
|
|
register int status;
|
|
|
|
status = vd_unlinkdrv(vdp, vdi);
|
|
if (status == 0) {
|
|
#ifdef OPENPROMS
|
|
status = vd_unconfigdrv(vdp, vdl->vdldrv_dev_ops);
|
|
#else
|
|
status = vd_unconfigdrv(vdp, vdl->vdldrv_mb_ctlr,
|
|
vdl->vdldrv_mb_driver, vdl->vdldrv_mb_device,
|
|
vdl->vdldrv_numctlrs, vdl->vdldrv_numdevs);
|
|
#endif OPENPROMS
|
|
}
|
|
return (status);
|
|
}
|
|
|
|
/*
|
|
* Link a driver into the system tables.
|
|
*/
|
|
int
|
|
vd_linkdrv(vdp)
|
|
struct vddrv *vdp;
|
|
{
|
|
register struct vdlinkage *vdl = vdp->vdd_vdtab;
|
|
register struct bdevsw *bdp = NULL;
|
|
register struct cdevsw *cdp = NULL;
|
|
register int s;
|
|
|
|
if (vdl->vdldrv_bdevsw) { /* find a bdevsw entry */
|
|
bdp = vd_getbdevsw(vdl->vdldrv_blockmajor);
|
|
if (bdp == NULL)
|
|
return (EINVAL);
|
|
}
|
|
|
|
if (vdl->vdldrv_cdevsw) { /* find a cdevsw entry */
|
|
cdp = vd_getcdevsw(vdl->vdldrv_charmajor);
|
|
if (cdp == NULL)
|
|
return (EINVAL);
|
|
}
|
|
|
|
/*
|
|
* Everything looks go.
|
|
*/
|
|
s = spl7();
|
|
if (bdp != NULL) {
|
|
*bdp = *vdl->vdldrv_bdevsw; /* setup bdevsw */
|
|
vdl->vdldrv_bdevsw = bdp;
|
|
vdl->vdldrv_blockmajor = bdp - bdevsw; /* set block major */
|
|
}
|
|
|
|
if (cdp != NULL) {
|
|
*cdp = *vdl->vdldrv_cdevsw; /* setup cdevsw */
|
|
vdl->vdldrv_cdevsw = cdp;
|
|
vdl->vdldrv_charmajor = cdp - cdevsw; /* set char major */
|
|
}
|
|
|
|
(void) splx(s);
|
|
return (0);
|
|
}
|
|
|
|
/*
|
|
* Unlink a driver from from the system.
|
|
*/
|
|
|
|
static struct bdevsw free_bdevsw_slot = {
|
|
vd_unuseddev, vd_unuseddev, /* d_open, d_close */
|
|
vd_unuseddev, vd_unuseddev, /* d_strategy, d_dump */
|
|
vd_unuseddev, 0 /* d_psize, d_flags */
|
|
};
|
|
|
|
static struct cdevsw free_cdevsw_slot = {
|
|
vd_unuseddev, vd_unuseddev, /* d_open, d_close */
|
|
vd_unuseddev, vd_unuseddev, /* d_read, d_write */
|
|
vd_unuseddev, vd_unuseddev, /* d_ioctl, d_reset */
|
|
vd_unuseddev, vd_unuseddev, /* d_select, d_mmap */
|
|
0 /* d_str */
|
|
};
|
|
|
|
/*ARGSUSED*/
|
|
int
|
|
vd_unlinkdrv(vdp, vdi)
|
|
struct vddrv *vdp;
|
|
struct vdioctl_unload *vdi;
|
|
{
|
|
register struct vdlinkage *vdl = vdp->vdd_vdtab;
|
|
register struct bdevsw *bdp = vdl->vdldrv_bdevsw;
|
|
register struct cdevsw *cdp = vdl->vdldrv_cdevsw;
|
|
register int s;
|
|
|
|
#ifdef UFS
|
|
/*
|
|
* Make sure there aren't filesystems mounted on this device.
|
|
*/
|
|
|
|
if (bdp != NULL && has_ufsmounted(vdl->vdldrv_blockmajor))
|
|
return (EBUSY);
|
|
#endif /* UFS */
|
|
|
|
s = spl7();
|
|
if (bdp != NULL)
|
|
*bdp = free_bdevsw_slot;
|
|
if (cdp != NULL)
|
|
*cdp = free_cdevsw_slot;
|
|
(void) splx(s);
|
|
return (0);
|
|
}
|
|
|
|
#ifdef UFS
|
|
/*
|
|
* Check whether any file systems are mounted on the specified block device.
|
|
*/
|
|
|
|
#include <ufs/mount.h>
|
|
|
|
int
|
|
has_ufsmounted(blockmajor)
|
|
register int blockmajor;
|
|
{
|
|
register struct mount *mp;
|
|
|
|
MLOCK();
|
|
for (mp = mounttab; mp != NULL; mp = mp->m_nxt) {
|
|
if (major(mp->m_dev) == blockmajor)
|
|
break;
|
|
}
|
|
MUNLOCK();
|
|
return (mp != NULL);
|
|
}
|
|
#endif /* UFS */
|
|
|
|
/*
|
|
* System call status info
|
|
*/
|
|
int
|
|
vd_statsys(vdp, vds)
|
|
struct vddrv *vdp;
|
|
struct vdstat *vds;
|
|
{
|
|
vds->vds_modinfo[0] = vdp->vdd_vdtab->vdlsys_num;
|
|
return (0);
|
|
}
|
|
|
|
/*
|
|
* Link a system call into the system by setting the proper sysent entry.
|
|
*/
|
|
|
|
int
|
|
vd_installsys(vdp, vdi)
|
|
struct vddrv *vdp;
|
|
struct vdioctl_load *vdi;
|
|
{
|
|
register struct sysent *sysp;
|
|
register struct vdlinkage *vdl = vdp->vdd_vdtab;
|
|
register struct vdconf *vdc;
|
|
register int status;
|
|
|
|
status = vd_addrcheck(vdp, (caddr_t) vdl->vdlsys_sysent);
|
|
if (status != 0)
|
|
return (status);
|
|
|
|
/*
|
|
* Get user config info
|
|
*/
|
|
if (vdi->vdi_userconf != NULL) {
|
|
vdc = vd_getvdconf(vdi->vdi_userconf);
|
|
if (vdc == NULL)
|
|
return (EFAULT);
|
|
if (vdc->vdc_type != VDCSYSCALLNUM)
|
|
return (EINVAL);
|
|
vdl->vdlsys_num = vdc->vdc_data;
|
|
}
|
|
|
|
sysp = vd_getsysent(vdl->vdlsys_num); /* get ptr to sysent entry */
|
|
if (sysp == NULL)
|
|
return (EINVAL); /* invalid system call number */
|
|
|
|
sysp->sy_narg = vdl->vdlsys_sysent->sy_narg;
|
|
sysp->sy_call = vdl->vdlsys_sysent->sy_call;
|
|
vdl->vdlsys_sysent = sysp;
|
|
vdl->vdlsys_num = sysp - sysent;
|
|
|
|
return (0);
|
|
}
|
|
|
|
/*
|
|
* Unlink a system call from the system.
|
|
*/
|
|
|
|
/*ARGSUSED*/
|
|
int
|
|
vd_removesys(vdp, vdi)
|
|
struct vddrv *vdp;
|
|
struct vdioctl_unload *vdi;
|
|
{
|
|
vdp->vdd_vdtab->vdlsys_sysent->sy_call = nosys;
|
|
vdp->vdd_vdtab->vdlsys_sysent->sy_narg = 0;
|
|
return (0);
|
|
}
|
|
|
|
/*
|
|
* Configure a network interface
|
|
*/
|
|
|
|
/*ARGSUSED*/
|
|
int
|
|
vd_installnet(vdp, vdi)
|
|
struct vddrv *vdp;
|
|
struct vdioctl_load *vdi;
|
|
{
|
|
register struct vdlinkage *vdl = vdp->vdd_vdtab;
|
|
|
|
#ifdef OPENPROMS
|
|
return (vd_configdrv(vdp, vdl->vdldrv_dev_ops));
|
|
#else
|
|
return (vd_configdrv(vdp, vdl->vdlnet_mb_ctlr,
|
|
vdl->vdlnet_mb_driver, vdl->vdlnet_mb_device,
|
|
vdl->vdlnet_numctlrs, vdl->vdlnet_numdevs));
|
|
#endif OPENPROMS
|
|
}
|
|
|
|
/*
|
|
* Remove a network interface
|
|
*/
|
|
|
|
/*ARGSUSED*/
|
|
int
|
|
vd_removenet(vdp, vdi)
|
|
struct vddrv *vdp;
|
|
struct vdioctl_unload *vdi;
|
|
{
|
|
register struct vdlinkage *vdl = vdp->vdd_vdtab;
|
|
register int status;
|
|
|
|
status = vd_unlinknet(vdp);
|
|
if (status == 0) {
|
|
#ifdef OPENPROMS
|
|
status = vd_unconfigdrv(vdp, vdl->vdldrv_dev_ops);
|
|
#else
|
|
status = vd_unconfigdrv(vdp, vdl->vdlnet_mb_ctlr,
|
|
vdl->vdlnet_mb_driver, vdl->vdlnet_mb_device,
|
|
vdl->vdlnet_numctlrs, vdl->vdlnet_numdevs);
|
|
#endif OPENPROMS
|
|
}
|
|
return (status);
|
|
|
|
}
|
|
|
|
/*
|
|
* Unlink a network interface from the system.
|
|
*/
|
|
|
|
int
|
|
vd_unlinknet(vdp)
|
|
struct vddrv *vdp;
|
|
{
|
|
register struct ifnet *ifp;
|
|
register struct ifnet *ifp_prev;
|
|
register struct ifaddr *ifa;
|
|
|
|
struct inpcb *inp, *head;
|
|
struct in_ifaddr *ia, *iaprev;
|
|
char ifnam[IFNAMSIZ];
|
|
char *s2;
|
|
int s;
|
|
|
|
extern struct inpcb udb;
|
|
extern struct inpcb tcb;
|
|
|
|
/*
|
|
* Get the interface name and make it into a name-unit array such
|
|
* as "ub0".
|
|
*/
|
|
#ifdef OPENPROMS
|
|
s2 = strcpy(ifnam, vdp->vdd_vdtab->vdlnet_dname);
|
|
#else
|
|
s2 = strcpy(ifnam, vdp->vdd_vdtab->vdlnet_mb_driver->mdr_dname);
|
|
#endif OPENPROMS
|
|
*s2++ = '0'; /* we only support unit 0 for now */
|
|
*s2 = '\0'; /* terminate the string */
|
|
|
|
/*
|
|
* Get the ifnet structure from the interface name-unit
|
|
*/
|
|
if ((ifp = ifunit(ifnam, strlen(ifnam))) == NULL) {
|
|
printf("vd_unlinknet: can't find %s\n", ifnam);
|
|
return (EINVAL);
|
|
}
|
|
|
|
if (ifp->if_flags & IFF_UP)
|
|
return (EBUSY);
|
|
|
|
s = spl7();
|
|
|
|
/*
|
|
* Flush inpcbs. Too bad we have to play all these games here.
|
|
* But the system wasn't designed to have drivers unloaded!!!
|
|
*/
|
|
head = &udb; /* start with udp inpcb list */
|
|
|
|
again:
|
|
for (inp = head->inp_next; inp != head; inp = inp->inp_next) {
|
|
if (inp->inp_route.ro_rt && inp->inp_route.ro_rt->rt_ifp == ifp)
|
|
in_losing(inp); /* flush routing info */
|
|
}
|
|
if (head == &udb) {
|
|
head = &tcb; /* flush tcp inpcb list */
|
|
goto again;
|
|
}
|
|
|
|
/*
|
|
* Flush the routing tables!!!
|
|
*/
|
|
for (ifa = ifp->if_addrlist; ifa; ifa = ifa->ifa_next) {
|
|
rtinit(&ifa->ifa_addr, &ifa->ifa_addr, SIOCDELRT, RTF_HOST);
|
|
rtinit(&ifa->ifa_addr, &ifa->ifa_addr, SIOCDELRT, 0);
|
|
}
|
|
|
|
/*
|
|
* Remove in_ifaddr entry (if any)
|
|
* ZZZ -- have to figure out how to m_free the in_ifaddr entry!!!!
|
|
*/
|
|
for (ia = in_ifaddr, iaprev = NULL; ia != NULL; ia = ia->ia_next) {
|
|
if (ia->ia_ifp == ifp) {
|
|
if (iaprev == NULL)
|
|
in_ifaddr = ia->ia_next;
|
|
else
|
|
iaprev->ia_next = ia->ia_next;
|
|
|
|
break;
|
|
}
|
|
iaprev = ia;
|
|
}
|
|
|
|
/*
|
|
* Now unlink our ifnet structure
|
|
*/
|
|
if (ifp == ifnet) {
|
|
ifnet = ifp->if_next;
|
|
} else {
|
|
ifp_prev = ifnet;
|
|
|
|
while (ifp_prev && ifp_prev->if_next != ifp)
|
|
ifp_prev = ifp_prev->if_next;
|
|
|
|
if (ifp_prev == NULL)
|
|
panic("vd_unlinknet");
|
|
|
|
ifp_prev->if_next = ifp->if_next;
|
|
}
|
|
(void) splx(s);
|
|
return (0);
|
|
}
|
|
|
|
#define empty(slot) ((slot) == nodev || (slot) == nulldev) || \
|
|
((slot) == seltrue || (slot) == 0)
|
|
|
|
/*
|
|
* Find a free bdevsw entry or check if the specified one if free.
|
|
*/
|
|
struct bdevsw *
|
|
vd_getbdevsw(major_dev)
|
|
register int major_dev;
|
|
{
|
|
register struct bdevsw *bdp;
|
|
|
|
if (major_dev < 0 || major_dev >= nblkdev)
|
|
return (NULL);
|
|
|
|
if (major_dev != 0) {
|
|
bdp = &bdevsw[major_dev];
|
|
/*
|
|
* If a major number is specified by the user and
|
|
* the bdevsw entry is not in use or it's one reserved
|
|
* for loadable drivers, then it's ok to use this one.
|
|
*/
|
|
return (bdp->d_open == vd_unuseddev ||
|
|
(empty(bdp->d_open) && empty(bdp->d_close) &&
|
|
empty(bdp->d_strategy) && empty(bdp->d_dump)))
|
|
? bdp : NULL;
|
|
}
|
|
|
|
for (bdp = bdevsw; bdp != &bdevsw[nblkdev]; bdp++) {
|
|
if (bdp->d_open == vd_unuseddev)
|
|
return (bdp); /* found an unused one */
|
|
}
|
|
return (NULL);
|
|
}
|
|
|
|
/*
|
|
* Find a free cdevsw entry or check if the specified one is free.
|
|
*/
|
|
struct cdevsw *
|
|
vd_getcdevsw(major_dev)
|
|
register int major_dev;
|
|
{
|
|
register struct cdevsw *cdp;
|
|
|
|
if (major_dev < 0 || major_dev >= nchrdev)
|
|
return (NULL);
|
|
|
|
if (major_dev != 0) {
|
|
cdp = &cdevsw[major_dev];
|
|
/*
|
|
* If a major number is specified by the user and
|
|
* the cdevsw entry is not in use or it's one reserved
|
|
* for loadable drivers, then it's ok to use this one.
|
|
*/
|
|
return (cdp->d_open == vd_unuseddev ||
|
|
(empty(cdp->d_open) && empty(cdp->d_close) &&
|
|
empty(cdp->d_read) && empty(cdp->d_write) &&
|
|
empty(cdp->d_ioctl) && empty(cdp->d_reset) &&
|
|
empty(cdp->d_select) && empty(cdp->d_mmap)))
|
|
? cdp : NULL;
|
|
}
|
|
|
|
for (cdp = cdevsw; cdp != &cdevsw[nchrdev]; cdp++) {
|
|
if (cdp->d_open == vd_unuseddev)
|
|
return (cdp); /* found an unused one */
|
|
}
|
|
return (NULL);
|
|
}
|
|
|
|
/*
|
|
* Find a free sysent entry or check if the specified one is free.
|
|
*/
|
|
struct sysent *
|
|
vd_getsysent(sysnum)
|
|
register int sysnum;
|
|
{
|
|
register int i;
|
|
|
|
extern int nsysent;
|
|
extern int firstvdsys;
|
|
extern int lastvdsys;
|
|
|
|
if (sysnum < 0 || sysnum >= nsysent)
|
|
return (NULL);
|
|
|
|
if (sysnum != 0)
|
|
return (sysent[sysnum].sy_call == nosys) ?
|
|
&sysent[sysnum] : NULL;
|
|
|
|
/*
|
|
* Find the next free system call number
|
|
*/
|
|
for (i = firstvdsys; i <= lastvdsys; i++) {
|
|
if (sysent[i].sy_call == nosys)
|
|
return (&sysent[i]);
|
|
}
|
|
return (NULL);
|
|
}
|
|
|
|
/*
|
|
* Routine address placed in devsw entries which are usable for
|
|
* a loadable driver.
|
|
*/
|
|
int
|
|
vd_unuseddev()
|
|
{
|
|
return (ENODEV);
|
|
}
|
|
|
|
/*
|
|
* Routine to verify that an address is valid.
|
|
*/
|
|
|
|
int
|
|
vd_addrcheck(vdp, addr)
|
|
register struct vddrv *vdp;
|
|
register caddr_t addr;
|
|
{
|
|
return (addr < vdp->vdd_vaddr ||
|
|
addr >= vdp->vdd_vaddr + vdp->vdd_size) ? EINVAL : 0;
|
|
}
|
|
|
|
struct vdconf *
|
|
vd_getvdconf(vdcp)
|
|
struct vdconf *vdcp;
|
|
{
|
|
static struct vdconf vdc;
|
|
|
|
return ((copyin((caddr_t)vdcp, (caddr_t)&vdc, sizeof vdc) == 0) ?
|
|
&vdc : NULL);
|
|
}
|
|
|
|
/*
|
|
* The remaining routines significantly differ between the mb and
|
|
* devinfo versions. Please try to keep them in sync.
|
|
*
|
|
* XXX - "OPENPROMS" is a misnomer, but there isn't a "DEVINFO".
|
|
*/
|
|
|
|
#ifdef OPENPROMS
|
|
|
|
/*
|
|
* Configure a new driver.
|
|
*
|
|
* Failure to successfully configure a controller or a device is not considered
|
|
* an error. However, if no devices or controllers come online then an
|
|
* error is returned.
|
|
*/
|
|
int
|
|
vd_configdrv(vdp, ops)
|
|
struct vddrv *vdp;
|
|
struct dev_ops *ops;
|
|
{
|
|
register int status = 0;
|
|
|
|
if (vd_checkdrvlinkage(vdp, ops) < 0)
|
|
return (EINVAL); /* bad linkage structure */
|
|
|
|
/*
|
|
* Configure the driver
|
|
* 1. Add the driver name to the av_nametab array (how?!) XXX
|
|
* 2. Add the ops vector to the ops list (do we really need to??) XXX
|
|
* 3. If not a pseudo-device, call add_drv to attach any
|
|
* devices to this driver. If no devices attach, return ENXIO
|
|
*/
|
|
|
|
/* XXX add the driver name to the av_nametab array ??? */
|
|
|
|
status = add_ops(ops);
|
|
if (status != 0)
|
|
return (status);
|
|
|
|
if (vdp->vdd_vdtab->vdl_magic != VDMAGIC_PSEUDO &&
|
|
add_drv(ops) <= 0) {
|
|
(void) rem_ops(ops);
|
|
return (ENXIO); /* no devices came up*/
|
|
}
|
|
|
|
return (0); /* success */
|
|
}
|
|
|
|
/*
|
|
* Unconfigure a driver.
|
|
*/
|
|
|
|
int
|
|
vd_unconfigdrv(vdp, ops)
|
|
struct vddrv *vdp;
|
|
struct dev_ops *ops;
|
|
{
|
|
register int s;
|
|
void rem_drv();
|
|
|
|
s = splx(pritospl(SPLMB));
|
|
|
|
if (vdp->vdd_userconf) {
|
|
vdp->vdd_userconf = 0;
|
|
vd_userunconfig(vdp);
|
|
}
|
|
|
|
if (vdp->vdd_vdtab->vdl_magic != VDMAGIC_PSEUDO)
|
|
rem_drv(ops);
|
|
|
|
(void) rem_ops(ops);
|
|
|
|
(void) splx(s);
|
|
return (0); /* success */
|
|
}
|
|
|
|
/*
|
|
* Make sure that the driver linkage structure looks ok.
|
|
* This is mostly for debugging to help the driver developer.
|
|
*/
|
|
|
|
int
|
|
vd_checkdrvlinkage(vdp, ops)
|
|
register struct vddrv *vdp;
|
|
struct dev_ops *ops;
|
|
{
|
|
if (vd_addrcheck(vdp, (caddr_t) ops) != 0)
|
|
return (EINVAL);
|
|
|
|
return (0);
|
|
}
|
|
|
|
/*
|
|
* Get user specified configuration information and put it in
|
|
* the vdlinkage structure.
|
|
*
|
|
* The user configuration is specified in an array of vdconf structures.
|
|
* Each entry has a type and a pointer to the structure appropriate for
|
|
* that type.
|
|
*/
|
|
int
|
|
vd_userconfig(vdp, vdi)
|
|
register struct vddrv *vdp;
|
|
struct vdioctl_load *vdi;
|
|
{
|
|
register struct vdconf *uvdc, *vdc;
|
|
|
|
uvdc = vdi->vdi_userconf;
|
|
vdc = vd_getvdconf(uvdc); /* get ptr to user config info */
|
|
|
|
/*
|
|
* Make a pass through the user configuration info. On this pass
|
|
* we count up the controllers and devices and set the block
|
|
* and character major numbers.
|
|
*/
|
|
while (vdc != NULL && vdc->vdc_type != VDCEND) {
|
|
switch (vdc->vdc_type) {
|
|
case VDCBLOCKMAJOR:
|
|
vdp->vdd_vdtab->vdldrv_blockmajor = vdc->vdc_data;
|
|
break;
|
|
case VDCCHARMAJOR:
|
|
vdp->vdd_vdtab->vdldrv_charmajor = vdc->vdc_data;
|
|
break;
|
|
default:
|
|
return (EINVAL);
|
|
}
|
|
vdc = vd_getvdconf(++uvdc);
|
|
}
|
|
|
|
if (vdc == NULL)
|
|
return (EFAULT);
|
|
|
|
vdp->vdd_userconf = 1; /* indicate we're using user conf info */
|
|
return (0);
|
|
}
|
|
|
|
/*
|
|
* Free user specified configuration information.
|
|
*/
|
|
/*ARGSUSED*/
|
|
void
|
|
vd_userunconfig(vdp)
|
|
struct vddrv *vdp;
|
|
{
|
|
/* nothing to do on a sun4c! */
|
|
}
|
|
|
|
#else OPENPROMS
|
|
|
|
/*
|
|
* Configure a new driver.
|
|
*
|
|
* Failure to successfully configure a controller or a device is not considered
|
|
* an error. However, if no devices or controllers come online then an
|
|
* error is returned.
|
|
*/
|
|
int
|
|
vd_configdrv(vdp, mc, mdr, md, numctlrs, numdevs)
|
|
struct vddrv *vdp;
|
|
struct mb_ctlr *mc;
|
|
struct mb_driver *mdr;
|
|
struct mb_device *md;
|
|
int numctlrs, numdevs;
|
|
{
|
|
int onlinectlrs, onlinedevs;
|
|
|
|
if (vdp->vdd_vdtab->vdl_magic == VDMAGIC_PSEUDO ||
|
|
md == NULL || mdr == NULL)
|
|
return (0); /* there's nothing to configure */
|
|
|
|
if (vd_checkdrvlinkage(vdp, mc, mdr, md, numctlrs, numdevs) < 0)
|
|
return (EINVAL); /* bad linkage structure */
|
|
|
|
/*
|
|
* Configure all controllers.
|
|
*/
|
|
if (numctlrs != 0) {
|
|
onlinectlrs = vd_confctlrs(mdr, mc, numctlrs);
|
|
if (onlinectlrs <= 0)
|
|
return (ENXIO);
|
|
}
|
|
|
|
/*
|
|
* Configure all devices.
|
|
*/
|
|
if (numdevs != 0) {
|
|
onlinedevs = vd_confdevs(mdr, mc, md, numdevs);
|
|
if (onlinedevs <= 0) {
|
|
if (onlinectlrs != 0) /* clean up */
|
|
(void) vd_unconfctlrs(mdr, mc, numctlrs);
|
|
return (ENXIO);
|
|
}
|
|
}
|
|
return (0); /* success */
|
|
}
|
|
|
|
/*
|
|
* Unconfigure all controllers and devices.
|
|
*/
|
|
|
|
int
|
|
vd_unconfigdrv(vdp, mc, mdr, md, numctlrs, numdevs)
|
|
struct vddrv *vdp;
|
|
struct mb_ctlr *mc;
|
|
struct mb_driver *mdr;
|
|
struct mb_device *md;
|
|
int numctlrs, numdevs;
|
|
{
|
|
register int s;
|
|
|
|
if (vdp->vdd_vdtab->vdl_magic == VDMAGIC_PSEUDO ||
|
|
md == NULL || mdr == NULL)
|
|
return (0); /* there's nothing to unconfigure */
|
|
|
|
s = splx(pritospl(SPLMB));
|
|
|
|
if (numctlrs != 0)
|
|
(void) vd_unconfctlrs(mdr, mc, numctlrs);
|
|
else if (numdevs != 0)
|
|
(void) vd_unconfdevs(mdr, mc, md, numdevs);
|
|
|
|
if (vdp->vdd_userconf) {
|
|
vdp->vdd_userconf = 0;
|
|
vd_userunconfig(vdp);
|
|
}
|
|
|
|
(void) splx(s);
|
|
return (0); /* success */
|
|
}
|
|
|
|
/*
|
|
* Make sure that the driver linkage structure looks ok.
|
|
* This is mostly for debugging to help the driver developer.
|
|
*/
|
|
|
|
#define VD_MAXCTLR 64 /* Max. no. of controllers */
|
|
#define VD_MAXDEVICE 64 /* Max. no. of devices */
|
|
|
|
int
|
|
vd_checkdrvlinkage(vdp, mc, mdr, md, numctlrs, numdevs)
|
|
register struct vddrv *vdp;
|
|
struct mb_ctlr *mc;
|
|
struct mb_driver *mdr;
|
|
struct mb_device *md;
|
|
int numctlrs;
|
|
int numdevs;
|
|
{
|
|
if (vd_addrcheck(vdp, (caddr_t) mdr) != 0)
|
|
return (EINVAL);
|
|
|
|
if (numctlrs < 0 || numctlrs > VD_MAXCTLR)
|
|
return (EINVAL);
|
|
|
|
if (numdevs < 0 || numdevs > VD_MAXDEVICE)
|
|
return (EINVAL);
|
|
|
|
if (numctlrs != 0 && mc == NULL)
|
|
return (EINVAL);
|
|
|
|
if (numdevs != 0 && md == NULL)
|
|
return (EINVAL);
|
|
|
|
if (numctlrs == 0 && mc != NULL)
|
|
return (EINVAL);
|
|
|
|
if (numdevs == 0 && md != NULL)
|
|
return (EINVAL);
|
|
|
|
if (mc != NULL && mc->mc_driver != mdr)
|
|
return (EINVAL);
|
|
|
|
if (md->md_driver != mdr)
|
|
return (EINVAL);
|
|
|
|
if (!vdp->vdd_userconf) {
|
|
if (mc != NULL && vd_addrcheck(vdp, (caddr_t) mc) != 0)
|
|
return (EINVAL);
|
|
else
|
|
return (vd_addrcheck(vdp, (caddr_t) md));
|
|
}
|
|
return (0);
|
|
}
|
|
|
|
/*
|
|
* Configure an array of controllers
|
|
*
|
|
* Returns the number of controllers that came online.
|
|
*/
|
|
int
|
|
vd_confctlrs(mdr, mc, numctlrs)
|
|
struct mb_driver *mdr;
|
|
register struct mb_ctlr *mc;
|
|
int numctlrs;
|
|
{
|
|
register struct mb_ctlr *mcend;
|
|
struct vec *v;
|
|
int onlinectlrs = 0;
|
|
int status, s;
|
|
|
|
s = splx(pritospl(SPLMB));
|
|
|
|
for (mcend = &mc[numctlrs]; mc < mcend; mc++) {
|
|
status = 0;
|
|
for (v = mc->mc_intr; v != NULL && v->v_func != NULL; v++) {
|
|
status = vd_checkirq(v, mc->mc_intpri, mc->mc_space);
|
|
if (status != 0)
|
|
break;
|
|
}
|
|
if (status == 0 && mbconfctrl(mc, mdr) != -1)
|
|
onlinectlrs++; /* one more is online */
|
|
}
|
|
|
|
(void) splx(s);
|
|
return (onlinectlrs);
|
|
}
|
|
|
|
/*
|
|
* Unconfigure an array of controllers
|
|
*/
|
|
int
|
|
vd_unconfctlrs(mdr, mc, numctlrs)
|
|
struct mb_driver *mdr;
|
|
register struct mb_ctlr *mc;
|
|
int numctlrs;
|
|
{
|
|
register struct mb_ctlr *mcend;
|
|
struct vec *v;
|
|
|
|
int extent, s;
|
|
|
|
s = splx(pritospl(SPLMB));
|
|
|
|
for (mcend = &mc[numctlrs]; mc < mcend; mc++) {
|
|
if (mc->mc_alive == 0)
|
|
continue; /* it's not online */
|
|
|
|
for (v = mc->mc_intr; v != NULL && v->v_func != NULL; v++)
|
|
vd_resetirq(v, mc->mc_intpri, mc->mc_space);
|
|
|
|
extent = btoc((int)mdr->mdr_size + ((int)mc->mc_addr&PGOFSET));
|
|
|
|
if (extent == 0)
|
|
extent = 1;
|
|
|
|
if (mc->mc_space != SP_VIRTUAL && mc->mc_space != SP_ATIO &&
|
|
mc->mc_space != SP_OBIO)
|
|
rmfree(kernelmap, (long)extent,
|
|
(u_long) btokmx(mc->mc_addr));
|
|
}
|
|
(void) splx(s);
|
|
return (0);
|
|
}
|
|
|
|
/*
|
|
* Configure an array of devices.
|
|
*
|
|
* Returns the number of devices that successfully came online.
|
|
*/
|
|
int
|
|
vd_confdevs(mdr, mc, md, numdevs)
|
|
struct mb_driver *mdr;
|
|
struct mb_ctlr *mc;
|
|
register struct mb_device *md;
|
|
int numdevs;
|
|
{
|
|
register struct mb_device *mdend;
|
|
struct vec *v;
|
|
|
|
int status;
|
|
int s;
|
|
int onlinedevs = 0;
|
|
|
|
mdend = &md[numdevs];
|
|
|
|
s = splx(pritospl(SPLMB));
|
|
|
|
for (; md < mdend; md++) {
|
|
if (md->md_ctlr != -1) {
|
|
if (mc == 0) {
|
|
printf(
|
|
"mb_device struct has md_ctlr == %d., but there are no controllers\n",
|
|
md->md_ctlr);
|
|
printf("skipping this device...\n");
|
|
continue;
|
|
}
|
|
if (mc[md->md_ctlr].mc_alive == 0)
|
|
continue; /* skip it, controller isn't up */
|
|
|
|
if (mbconfdev(md, &mc[md->md_ctlr], mdr) == -1)
|
|
continue; /* skip it, device didn't come up */
|
|
} else {
|
|
status = 0;
|
|
for (v = md->md_intr;
|
|
v != NULL && v->v_func != NULL; v++) {
|
|
status = vd_checkirq(
|
|
v, md->md_intpri, md->md_space);
|
|
if (status != 0)
|
|
break;
|
|
}
|
|
if (status != 0 || mbconfnonms(md, mdr) == -1)
|
|
continue; /* skip it, device didn't come up */
|
|
}
|
|
onlinedevs++; /* one more is online */
|
|
}
|
|
|
|
(void) splx(s);
|
|
return (onlinedevs);
|
|
}
|
|
|
|
/*
|
|
* Unconfigure an array of devices.
|
|
* Only do this if the devices aren't attached to a controller.
|
|
*/
|
|
int
|
|
vd_unconfdevs(mdr, mc, md, numdevs)
|
|
struct mb_driver *mdr;
|
|
struct mb_ctlr *mc;
|
|
register struct mb_device *md;
|
|
int numdevs;
|
|
{
|
|
register struct mb_device *mdend;
|
|
struct vec *v;
|
|
int extent;
|
|
int s;
|
|
|
|
if (mc != NULL && md->md_ctlr != -1)
|
|
return (0); /* this device is on a controller */
|
|
|
|
s = splx(pritospl(SPLMB));
|
|
|
|
mdend = &md[numdevs];
|
|
|
|
for (; md < mdend; md++) {
|
|
for (v = md->md_intr; v != NULL && v->v_func != NULL; v++)
|
|
vd_resetirq(v, md->md_intpri, md->md_space);
|
|
|
|
extent = btoc((int)mdr->mdr_size + ((int)md->md_addr&PGOFSET));
|
|
if (extent == 0)
|
|
extent = 1;
|
|
|
|
if (md->md_space != SP_VIRTUAL && md->md_space != SP_ATIO &&
|
|
md->md_space != SP_OBIO)
|
|
rmfree(kernelmap, (long)extent,
|
|
(u_long) btokmx(md->md_addr));
|
|
}
|
|
|
|
(void) splx(s);
|
|
return (0);
|
|
}
|
|
|
|
/*
|
|
* Get user specified configuration information and put it in
|
|
* the vdlinkage structure.
|
|
*
|
|
* The user configuration is specified in an array of vdconf structures.
|
|
* Each entry has a type and a pointer to the structure appropriate for
|
|
* that type.
|
|
*/
|
|
int
|
|
vd_userconfig(vdp, vdi)
|
|
register struct vddrv *vdp;
|
|
struct vdioctl_load *vdi;
|
|
{
|
|
register struct vdconf *uvdc, *vdc;
|
|
struct mb_ctlr *mc;
|
|
struct mb_device *md;
|
|
caddr_t mbp;
|
|
int numctlrs = 0;
|
|
int numdevs = 0;
|
|
|
|
uvdc = vdi->vdi_userconf;
|
|
vdc = vd_getvdconf(uvdc); /* get ptr to user config info */
|
|
|
|
/*
|
|
* Make a pass through the user configuration info. On this pass
|
|
* we count up the controllers and devices and set the block
|
|
* and character major numbers.
|
|
*/
|
|
while (vdc != NULL && vdc->vdc_type != VDCEND) {
|
|
switch (vdc->vdc_type) {
|
|
case VDCCONTROLLER:
|
|
numctlrs++; /* one more controller */
|
|
break;
|
|
case VDCDEVICE:
|
|
numdevs++; /* one more device */
|
|
break;
|
|
case VDCBLOCKMAJOR:
|
|
vdp->vdd_vdtab->vdldrv_blockmajor = vdc->vdc_data;
|
|
break;
|
|
case VDCCHARMAJOR:
|
|
vdp->vdd_vdtab->vdldrv_charmajor = vdc->vdc_data;
|
|
break;
|
|
default:
|
|
return (EINVAL);
|
|
}
|
|
vdc = vd_getvdconf(++uvdc);
|
|
}
|
|
|
|
if (vdc == NULL)
|
|
return (EFAULT);
|
|
if (numctlrs == 0 && numdevs == 0)
|
|
return (0); /* nothing to do */
|
|
|
|
/*
|
|
* Allocate the controller and device tables.
|
|
*/
|
|
mbp = new_kmem_alloc((u_int)(numctlrs * sizeof (struct mb_ctlr) +
|
|
numdevs * sizeof (struct mb_device)), KMEM_SLEEP);
|
|
|
|
mc = NULL;
|
|
md = NULL;
|
|
|
|
vdp->vdd_vdtab->vdldrv_numctlrs = numctlrs;
|
|
vdp->vdd_vdtab->vdldrv_numdevs = numdevs;
|
|
|
|
if (numctlrs) {
|
|
mc = (struct mb_ctlr *)mbp;
|
|
mbp = (caddr_t)((struct mb_ctlr *)mbp + numctlrs);
|
|
}
|
|
|
|
if (numdevs)
|
|
md = (struct mb_device *)mbp;
|
|
|
|
vdp->vdd_vdtab->vdldrv_mb_ctlr = mc;
|
|
vdp->vdd_vdtab->vdldrv_mb_device = md;
|
|
|
|
uvdc = vdi->vdi_userconf; /* get start of user config info again */
|
|
vdc = vd_getvdconf(uvdc);
|
|
|
|
/*
|
|
* Make a second pass on the user configuration information.
|
|
* During this pass, we copy the controller and device information
|
|
* from the user's space to kernel allocated structures.
|
|
*/
|
|
while (vdc != NULL && vdc->vdc_type != VDCEND) {
|
|
switch (vdc->vdc_type) {
|
|
case VDCCONTROLLER:
|
|
/*
|
|
* Copy mb_ctrl structure from the user.
|
|
*/
|
|
if (copyin((caddr_t)vdc->vdc_data, (caddr_t) mc,
|
|
sizeof (struct mb_ctlr)) != 0) {
|
|
return (EFAULT);
|
|
}
|
|
mc->mc_driver = vdp->vdd_vdtab->vdldrv_mb_driver;
|
|
#ifndef sun386
|
|
if (mc->mc_intr != NULL) {
|
|
mc->mc_intr = allocvec(mc->mc_intr);
|
|
if (mc->mc_intr == NULL)
|
|
return (EFAULT);
|
|
}
|
|
#endif /* !sun386 */
|
|
mc++; /* next controller structure */
|
|
break;
|
|
|
|
case VDCDEVICE:
|
|
/*
|
|
* Copy mb_device structure from the user.
|
|
*/
|
|
if (copyin((caddr_t)vdc->vdc_data, (caddr_t) md,
|
|
sizeof (struct mb_device)) != 0) {
|
|
return (EFAULT);
|
|
}
|
|
md->md_driver = vdp->vdd_vdtab->vdldrv_mb_driver;
|
|
#ifndef sun386
|
|
if (md->md_intr != NULL) {
|
|
md->md_intr = allocvec(md->md_intr);
|
|
if (md->md_intr == NULL)
|
|
return (EFAULT);
|
|
}
|
|
#endif /* !sun386 */
|
|
md++; /* next device structure */
|
|
break;
|
|
}
|
|
vdc = vd_getvdconf(++uvdc);
|
|
}
|
|
|
|
vdp->vdd_userconf = 1; /* indicate we're using user conf info */
|
|
return (0);
|
|
}
|
|
|
|
#ifndef sun386
|
|
/*
|
|
* Allocate vec structures for interrupt linkage
|
|
*/
|
|
struct vec *
|
|
allocvec(uvec)
|
|
struct vec *uvec;
|
|
{
|
|
register int i, nvec;
|
|
struct vec *kvec;
|
|
int *kval;
|
|
caddr_t vp;
|
|
unsigned vsize;
|
|
register struct vec *v;
|
|
struct vec tmpvec;
|
|
|
|
for (v = uvec, nvec = 0; /* empty */; v++, nvec++) {
|
|
if (copyin((caddr_t)v, (caddr_t)&tmpvec, sizeof tmpvec) != 0)
|
|
return (NULL);
|
|
if (tmpvec.v_func == NULL)
|
|
break;
|
|
}
|
|
vsize = (nvec + 1) * sizeof (struct vec) + nvec * sizeof (int);
|
|
vp = new_kmem_alloc(vsize, KMEM_SLEEP);
|
|
kvec = (struct vec *)vp;
|
|
kval = (int *)(vp + (nvec + 1) * sizeof (struct vec));
|
|
|
|
/*
|
|
* Copy user vector structure
|
|
*/
|
|
if (copyin((caddr_t)uvec, (caddr_t)kvec,
|
|
(u_int)(nvec * sizeof (struct vec))) != 0) {
|
|
kmem_free(vp, vsize);
|
|
return (NULL);
|
|
}
|
|
|
|
/*
|
|
* Copy user values & finish initializing kvec
|
|
*/
|
|
for (i = 0; i != nvec; i++) {
|
|
if (copyin((caddr_t)&uvec->v_vptr[i], (caddr_t)&kval[i],
|
|
sizeof (int)) != 0) {
|
|
kmem_free(vp, vsize);
|
|
return (NULL);
|
|
}
|
|
kvec[i].v_vptr = &kval[i];
|
|
}
|
|
kvec[nvec].v_func = NULL;
|
|
|
|
return (kvec);
|
|
}
|
|
|
|
/*
|
|
* Free previously allocated vec structures
|
|
*/
|
|
void
|
|
freevec(vec)
|
|
struct vec *vec;
|
|
{
|
|
register int nvec;
|
|
register struct vec *v;
|
|
|
|
for (v = vec; v->v_func != NULL; v++)
|
|
;
|
|
|
|
nvec = v - vec;
|
|
kmem_free((caddr_t)vec, (u_int) ((nvec + 1) * sizeof (struct vec)
|
|
+ nvec * sizeof (int)));
|
|
}
|
|
#endif /* !sun386 */
|
|
|
|
/*
|
|
* Free user specified configuration information.
|
|
*/
|
|
void
|
|
vd_userunconfig(vdp)
|
|
struct vddrv *vdp;
|
|
{
|
|
struct vdlinkage *vdl = vdp->vdd_vdtab;
|
|
register struct mb_ctlr *vd_mc = vdl->vdldrv_mb_ctlr, *mc;
|
|
register struct mb_device *vd_md = vdl->vdldrv_mb_device, *md;
|
|
register int nctlr = vdl->vdldrv_numctlrs;
|
|
register int ndev = vdl->vdldrv_numdevs;
|
|
u_int allocsize;
|
|
|
|
#ifndef sun386
|
|
for (mc = vd_mc; mc != vd_mc + nctlr; mc++) {
|
|
if (mc->mc_intr != NULL)
|
|
freevec(mc->mc_intr);
|
|
}
|
|
|
|
for (md = vd_md; md != vd_md + ndev; md++) {
|
|
if (md->md_intr != NULL)
|
|
freevec(md->md_intr);
|
|
}
|
|
#endif /* !sun386 */
|
|
|
|
allocsize = nctlr * sizeof (struct mb_ctlr)
|
|
+ ndev * sizeof (struct mb_device);
|
|
if (nctlr != 0)
|
|
kmem_free((caddr_t)vd_mc, allocsize);
|
|
else
|
|
kmem_free((caddr_t)vd_md, allocsize);
|
|
}
|
|
|
|
#endif OPENPROMS
|
|
|
|
#endif /* VDDRV */
|