|
Packit |
d36e9b |
/*
|
|
Packit |
d36e9b |
* Driver for IO port access
|
|
Packit |
d36e9b |
*
|
|
Packit |
d36e9b |
* PC-DOS compatibility requirements:
|
|
Packit |
d36e9b |
* Nearly all locations have defined values, see
|
|
Packit |
d36e9b |
* the PC AT Hardware reference manual for details.
|
|
Packit |
d36e9b |
*
|
|
Packit |
d36e9b |
*/
|
|
Packit |
d36e9b |
|
|
Packit |
d36e9b |
#include <sys/param.h>
|
|
Packit |
d36e9b |
#include <sys/types.h>
|
|
Packit |
d36e9b |
#include <sys/sysmacros.h>
|
|
Packit |
d36e9b |
#if 0
|
|
Packit |
d36e9b |
#include <sys/dir.h>
|
|
Packit |
d36e9b |
#endif
|
|
Packit |
d36e9b |
#include <sys/signal.h>
|
|
Packit |
d36e9b |
#include <sys/systm.h>
|
|
Packit |
d36e9b |
#include <sys/user.h>
|
|
Packit |
d36e9b |
#include <sys/errno.h>
|
|
Packit |
d36e9b |
#include <sys/file.h>
|
|
Packit |
d36e9b |
#include <sys/modctl.h>
|
|
Packit |
d36e9b |
#include <sys/open.h>
|
|
Packit |
d36e9b |
#include <sys/stat.h>
|
|
Packit |
d36e9b |
#include <sys/conf.h>
|
|
Packit |
d36e9b |
#include <sys/cmn_err.h>
|
|
Packit |
d36e9b |
#include <sys/ddi.h>
|
|
Packit |
d36e9b |
#include <sys/sunddi.h>
|
|
Packit |
d36e9b |
|
|
Packit |
d36e9b |
static void *state_head; /* opaque handle top of state structs */
|
|
Packit |
d36e9b |
|
|
Packit |
d36e9b |
/* Prototypes */
|
|
Packit |
d36e9b |
|
|
Packit |
d36e9b |
static int
|
|
Packit |
d36e9b |
iopattach(dev_info_t *dip, ddi_attach_cmd_t cmd);
|
|
Packit |
d36e9b |
|
|
Packit |
d36e9b |
static int
|
|
Packit |
d36e9b |
iopgetinfo(dev_info_t *dip, ddi_info_cmd_t cmd, void *arg, void **resultp);
|
|
Packit |
d36e9b |
|
|
Packit |
d36e9b |
static int
|
|
Packit |
d36e9b |
iopdetach(dev_info_t *dip, ddi_detach_cmd_t cmd);
|
|
Packit |
d36e9b |
|
|
Packit |
d36e9b |
static int
|
|
Packit |
d36e9b |
iopopen(dev_t *devp, int flag, int otyp, cred_t *credp);
|
|
Packit |
d36e9b |
|
|
Packit |
d36e9b |
static int
|
|
Packit |
d36e9b |
iopclose(dev_t dev, int openflags, int otyp, cred_t *credp);
|
|
Packit |
d36e9b |
|
|
Packit |
d36e9b |
static int
|
|
Packit |
d36e9b |
iopioctl(dev_t dev,
|
|
Packit |
d36e9b |
int cmd,
|
|
Packit |
d36e9b |
intptr_t arg,
|
|
Packit |
d36e9b |
int mode,
|
|
Packit |
d36e9b |
cred_t *credp,
|
|
Packit |
d36e9b |
int *rvalp);
|
|
Packit |
d36e9b |
|
|
Packit |
d36e9b |
/* device operations */
|
|
Packit |
d36e9b |
#define IOPREAD 1
|
|
Packit |
d36e9b |
#define IOPWRITE 2
|
|
Packit |
d36e9b |
|
|
Packit |
d36e9b |
typedef struct iop_struct { /* per-unit structure */
|
|
Packit |
d36e9b |
dev_info_t *dip;
|
|
Packit |
d36e9b |
kmutex_t mutex;
|
|
Packit |
d36e9b |
} iop;
|
|
Packit |
d36e9b |
|
|
Packit |
d36e9b |
typedef struct iopbuf_struct{
|
|
Packit |
d36e9b |
unsigned int port;
|
|
Packit |
d36e9b |
unsigned char port_value;
|
|
Packit |
d36e9b |
} iopbuf;
|
|
Packit |
d36e9b |
|
|
Packit |
d36e9b |
static struct cb_ops iop_cb_ops = {
|
|
Packit |
d36e9b |
iopopen,
|
|
Packit |
d36e9b |
iopclose,
|
|
Packit |
d36e9b |
nodev, /* not a block driver */
|
|
Packit |
d36e9b |
nodev, /* no print */
|
|
Packit |
d36e9b |
nodev, /* no dump */
|
|
Packit |
d36e9b |
nodev, /* no read */
|
|
Packit |
d36e9b |
nodev, /* no write */
|
|
Packit |
d36e9b |
iopioctl,
|
|
Packit |
d36e9b |
nodev, /* no devmap */
|
|
Packit |
d36e9b |
nodev, /* no mmap */
|
|
Packit |
d36e9b |
nodev, /* no segmap */
|
|
Packit |
d36e9b |
nochpoll, /* no chpoll */
|
|
Packit |
d36e9b |
ddi_prop_op,
|
|
Packit |
d36e9b |
0, /* not a STREAMS driver */
|
|
Packit |
d36e9b |
D_NEW | D_MP, /* (hopefully) MT and MP safe */
|
|
Packit |
d36e9b |
};
|
|
Packit |
d36e9b |
|
|
Packit |
d36e9b |
static struct dev_ops iop_ops =
|
|
Packit |
d36e9b |
{
|
|
Packit |
d36e9b |
DEVO_REV, /* devo_rev */
|
|
Packit |
d36e9b |
0, /* devo_refcnt */
|
|
Packit |
d36e9b |
iopgetinfo, /* devo_getinfo */
|
|
Packit |
d36e9b |
nulldev, /* devo_identify */
|
|
Packit |
d36e9b |
nulldev, /* devo_probe */
|
|
Packit |
d36e9b |
iopattach, /* devo_attach */
|
|
Packit |
d36e9b |
iopdetach, /* devo_detach */
|
|
Packit |
d36e9b |
nodev, /* devo_reset */
|
|
Packit |
d36e9b |
&iop_cb_ops, /* devo_cb_ops */
|
|
Packit |
d36e9b |
(struct bus_ops *)0, /* devo_bus_ops */
|
|
Packit |
d36e9b |
nodev /* devo_power */
|
|
Packit |
d36e9b |
};
|
|
Packit |
d36e9b |
|
|
Packit |
d36e9b |
extern struct mod_ops mod_driverops;
|
|
Packit |
d36e9b |
|
|
Packit |
d36e9b |
static struct modldrv modldrv = {
|
|
Packit |
d36e9b |
&mod_driverops,
|
|
Packit |
d36e9b |
"Simon's IO Port driver 0.9",
|
|
Packit |
d36e9b |
&iop_ops
|
|
Packit |
d36e9b |
};
|
|
Packit |
d36e9b |
|
|
Packit |
d36e9b |
static struct modlinkage modlinkage = {
|
|
Packit |
d36e9b |
MODREV_1,
|
|
Packit |
d36e9b |
{(void *)&modldrv, NULL,}
|
|
Packit |
d36e9b |
};
|
|
Packit |
d36e9b |
|
|
Packit |
d36e9b |
/* Globally exported functions */
|
|
Packit |
d36e9b |
int
|
|
Packit |
d36e9b |
_init(void)
|
|
Packit |
d36e9b |
{
|
|
Packit |
d36e9b |
int error;
|
|
Packit |
d36e9b |
|
|
Packit |
d36e9b |
#ifdef DEBUG
|
|
Packit |
d36e9b |
cmn_err(CE_CONT, "_init: start\n");
|
|
Packit |
d36e9b |
#endif
|
|
Packit |
d36e9b |
if ((error = ddi_soft_state_init(&state_head, sizeof(iop), 1)) != 0) {
|
|
Packit |
d36e9b |
#ifdef DEBUG
|
|
Packit |
d36e9b |
cmn_err(CE_CONT, "_init: couldn't ddi_soft_state_init\n");
|
|
Packit |
d36e9b |
#endif
|
|
Packit |
d36e9b |
return error;
|
|
Packit |
d36e9b |
}
|
|
Packit |
d36e9b |
if ((error = mod_install(&modlinkage)) != 0) {
|
|
Packit |
d36e9b |
#ifdef DEBUG
|
|
Packit |
d36e9b |
cmn_err(CE_CONT, "_init: couldn't mod_install\n");
|
|
Packit |
d36e9b |
#endif
|
|
Packit |
d36e9b |
ddi_soft_state_fini(&state_head);
|
|
Packit |
d36e9b |
}
|
|
Packit |
d36e9b |
#ifdef DEBUG
|
|
Packit |
d36e9b |
cmn_err(CE_CONT, "_init: done\n");
|
|
Packit |
d36e9b |
#endif
|
|
Packit |
d36e9b |
|
|
Packit |
d36e9b |
return error;
|
|
Packit |
d36e9b |
}
|
|
Packit |
d36e9b |
|
|
Packit |
d36e9b |
int
|
|
Packit |
d36e9b |
_info(struct modinfo *modinfop)
|
|
Packit |
d36e9b |
{
|
|
Packit |
d36e9b |
int retval;
|
|
Packit |
d36e9b |
|
|
Packit |
d36e9b |
#ifdef DEBUG
|
|
Packit |
d36e9b |
cmn_err(CE_CONT, "_info: start\n");
|
|
Packit |
d36e9b |
#endif
|
|
Packit |
d36e9b |
retval = mod_info(&modlinkage, modinfop);
|
|
Packit |
d36e9b |
#ifdef DEBUG
|
|
Packit |
d36e9b |
cmn_err(CE_CONT, "_info: done\n");
|
|
Packit |
d36e9b |
#endif
|
|
Packit |
d36e9b |
return retval;
|
|
Packit |
d36e9b |
}
|
|
Packit |
d36e9b |
|
|
Packit |
d36e9b |
int
|
|
Packit |
d36e9b |
_fini(void)
|
|
Packit |
d36e9b |
{
|
|
Packit |
d36e9b |
int status;
|
|
Packit |
d36e9b |
|
|
Packit |
d36e9b |
#ifdef DEBUG
|
|
Packit |
d36e9b |
cmn_err(CE_CONT, "_fini: start\n");
|
|
Packit |
d36e9b |
#endif
|
|
Packit |
d36e9b |
if ((status = mod_remove(&modlinkage)) != 0) {
|
|
Packit |
d36e9b |
#ifdef DEBUG
|
|
Packit |
d36e9b |
cmn_err(CE_CONT, "_fini: couldn't mod_remove\n");
|
|
Packit |
d36e9b |
#endif
|
|
Packit |
d36e9b |
return status;
|
|
Packit |
d36e9b |
}
|
|
Packit |
d36e9b |
ddi_soft_state_fini(&state_head);
|
|
Packit |
d36e9b |
#ifdef DEBUG
|
|
Packit |
d36e9b |
cmn_err(CE_CONT, "_fini: done\n");
|
|
Packit |
d36e9b |
#endif
|
|
Packit |
d36e9b |
return status;
|
|
Packit |
d36e9b |
}
|
|
Packit |
d36e9b |
|
|
Packit |
d36e9b |
static int
|
|
Packit |
d36e9b |
iopattach(dev_info_t *dip, ddi_attach_cmd_t cmd)
|
|
Packit |
d36e9b |
{
|
|
Packit |
d36e9b |
int instance;
|
|
Packit |
d36e9b |
iop *iop_p;
|
|
Packit |
d36e9b |
|
|
Packit |
d36e9b |
switch(cmd) {
|
|
Packit |
d36e9b |
case DDI_ATTACH:
|
|
Packit |
d36e9b |
break;
|
|
Packit |
d36e9b |
default:
|
|
Packit |
d36e9b |
return DDI_FAILURE;
|
|
Packit |
d36e9b |
}
|
|
Packit |
d36e9b |
instance = ddi_get_instance(dip);
|
|
Packit |
d36e9b |
#ifdef DEBUG
|
|
Packit |
d36e9b |
cmn_err(CE_CONT, "iopattach: start, instance = %d\n", instance);
|
|
Packit |
d36e9b |
#endif
|
|
Packit |
d36e9b |
if (ddi_soft_state_zalloc(state_head, instance) != 0) {
|
|
Packit |
d36e9b |
#ifdef DEBUG
|
|
Packit |
d36e9b |
cmn_err(CE_CONT, "iopattach: ddi_soft_state_zalloc failed\n");
|
|
Packit |
d36e9b |
#endif
|
|
Packit |
d36e9b |
return DDI_FAILURE;
|
|
Packit |
d36e9b |
}
|
|
Packit |
d36e9b |
iop_p = (iop *)ddi_get_soft_state(state_head, instance);
|
|
Packit |
d36e9b |
ddi_set_driver_private(dip, (caddr_t)iop_p);
|
|
Packit |
d36e9b |
iop_p->dip = dip;
|
|
Packit |
d36e9b |
mutex_init(&iop_p->mutex, "iop mutex", MUTEX_DRIVER, (void *)0);
|
|
Packit |
d36e9b |
if (ddi_create_minor_node(dip, ddi_get_name(dip), S_IFCHR, instance,
|
|
Packit |
d36e9b |
DDI_PSEUDO, 0) == DDI_FAILURE)
|
|
Packit |
d36e9b |
{
|
|
Packit |
d36e9b |
mutex_destroy(&iop_p->mutex);
|
|
Packit |
d36e9b |
ddi_soft_state_free(state_head, instance);
|
|
Packit |
d36e9b |
#ifdef DEBUG
|
|
Packit |
d36e9b |
cmn_err(CE_CONT, "iopattach: ddi_create_minor_node failed\n");
|
|
Packit |
d36e9b |
#endif
|
|
Packit |
d36e9b |
return DDI_FAILURE;
|
|
Packit |
d36e9b |
}
|
|
Packit |
d36e9b |
ddi_report_dev(dip);
|
|
Packit |
d36e9b |
#ifdef DEBUG
|
|
Packit |
d36e9b |
cmn_err(CE_CONT, "iopattach: done\n");
|
|
Packit |
d36e9b |
#endif
|
|
Packit |
d36e9b |
return DDI_SUCCESS;
|
|
Packit |
d36e9b |
}
|
|
Packit |
d36e9b |
|
|
Packit |
d36e9b |
static int
|
|
Packit |
d36e9b |
iopgetinfo(dev_info_t *dip, ddi_info_cmd_t cmd, void *arg, void **resultp)
|
|
Packit |
d36e9b |
{
|
|
Packit |
d36e9b |
int error;
|
|
Packit |
d36e9b |
iop *iop_p;
|
|
Packit |
d36e9b |
|
|
Packit |
d36e9b |
#ifdef DEBUG
|
|
Packit |
d36e9b |
cmn_err(CE_CONT, "iopgetinfo: start\n");
|
|
Packit |
d36e9b |
#endif
|
|
Packit |
d36e9b |
switch(cmd) {
|
|
Packit |
d36e9b |
case DDI_INFO_DEVT2DEVINFO:
|
|
Packit |
d36e9b |
iop_p = (iop *)ddi_get_soft_state(state_head,
|
|
Packit |
d36e9b |
getminor((dev_t)arg));
|
|
Packit |
d36e9b |
if (iop_p == NULL) {
|
|
Packit |
d36e9b |
*resultp = NULL;
|
|
Packit |
d36e9b |
#ifdef DEBUG
|
|
Packit |
d36e9b |
cmn_err(CE_CONT, "iopgetinfo: ddi_get_soft_state failed\n");
|
|
Packit |
d36e9b |
#endif
|
|
Packit |
d36e9b |
error = DDI_FAILURE;
|
|
Packit |
d36e9b |
} else {
|
|
Packit |
d36e9b |
mutex_enter(&iop_p->mutex);
|
|
Packit |
d36e9b |
*resultp = iop_p->dip;
|
|
Packit |
d36e9b |
mutex_exit(&iop_p->mutex);
|
|
Packit |
d36e9b |
error = DDI_SUCCESS;
|
|
Packit |
d36e9b |
}
|
|
Packit |
d36e9b |
break;
|
|
Packit |
d36e9b |
case DDI_INFO_DEVT2INSTANCE:
|
|
Packit |
d36e9b |
*resultp = (void *)getminor((dev_t)arg);
|
|
Packit |
d36e9b |
error = DDI_SUCCESS;
|
|
Packit |
d36e9b |
break;
|
|
Packit |
d36e9b |
default:
|
|
Packit |
d36e9b |
*resultp = NULL;
|
|
Packit |
d36e9b |
return DDI_FAILURE;
|
|
Packit |
d36e9b |
}
|
|
Packit |
d36e9b |
#ifdef DEBUG
|
|
Packit |
d36e9b |
cmn_err(CE_CONT, "iopgetinfo: done\n");
|
|
Packit |
d36e9b |
#endif
|
|
Packit |
d36e9b |
return error;
|
|
Packit |
d36e9b |
}
|
|
Packit |
d36e9b |
|
|
Packit |
d36e9b |
static int
|
|
Packit |
d36e9b |
iopdetach(dev_info_t *dip, ddi_detach_cmd_t cmd)
|
|
Packit |
d36e9b |
{
|
|
Packit |
d36e9b |
iop *iop_p;
|
|
Packit |
d36e9b |
int instance;
|
|
Packit |
d36e9b |
|
|
Packit |
d36e9b |
#ifdef DEBUG
|
|
Packit |
d36e9b |
cmn_err(CE_CONT, "iopdetach: start\n");
|
|
Packit |
d36e9b |
#endif
|
|
Packit |
d36e9b |
switch(cmd) {
|
|
Packit |
d36e9b |
case DDI_DETACH:
|
|
Packit |
d36e9b |
break;
|
|
Packit |
d36e9b |
default:
|
|
Packit |
d36e9b |
return DDI_FAILURE;
|
|
Packit |
d36e9b |
}
|
|
Packit |
d36e9b |
|
|
Packit |
d36e9b |
instance = ddi_get_instance(dip);
|
|
Packit |
d36e9b |
iop_p = (iop *)ddi_get_soft_state(state_head, instance);
|
|
Packit |
d36e9b |
ddi_remove_minor_node(dip, NULL);
|
|
Packit |
d36e9b |
mutex_destroy(&iop_p->mutex);
|
|
Packit |
d36e9b |
ddi_soft_state_free(state_head, instance);
|
|
Packit |
d36e9b |
#ifdef DEBUG
|
|
Packit |
d36e9b |
cmn_err(CE_CONT, "iopdetach: done\n");
|
|
Packit |
d36e9b |
#endif
|
|
Packit |
d36e9b |
return DDI_SUCCESS;
|
|
Packit |
d36e9b |
}
|
|
Packit |
d36e9b |
|
|
Packit |
d36e9b |
static int
|
|
Packit |
d36e9b |
iopopen(dev_t *devp, int flag, int otyp, cred_t *credp)
|
|
Packit |
d36e9b |
{
|
|
Packit |
d36e9b |
int retval = 0;
|
|
Packit |
d36e9b |
iop *iop_p;
|
|
Packit |
d36e9b |
|
|
Packit |
d36e9b |
iop_p = (iop *)ddi_get_soft_state(state_head, getminor(*devp));
|
|
Packit |
d36e9b |
|
|
Packit |
d36e9b |
if (iop_p == NULL) {
|
|
Packit |
d36e9b |
return ENXIO;
|
|
Packit |
d36e9b |
}
|
|
Packit |
d36e9b |
|
|
Packit |
d36e9b |
if (otyp != OTYP_CHR) {
|
|
Packit |
d36e9b |
return EINVAL;
|
|
Packit |
d36e9b |
}
|
|
Packit |
d36e9b |
|
|
Packit |
d36e9b |
if ((flag & FWRITE) &&
|
|
Packit |
d36e9b |
(drv_priv(credp) != 0))
|
|
Packit |
d36e9b |
{
|
|
Packit |
d36e9b |
retval = EACCES;
|
|
Packit |
d36e9b |
} else {
|
|
Packit |
d36e9b |
retval = 0;
|
|
Packit |
d36e9b |
}
|
|
Packit |
d36e9b |
return retval;
|
|
Packit |
d36e9b |
}
|
|
Packit |
d36e9b |
|
|
Packit |
d36e9b |
static int
|
|
Packit |
d36e9b |
iopclose(dev_t dev, int openflags, int otyp, cred_t *credp)
|
|
Packit |
d36e9b |
{
|
|
Packit |
d36e9b |
iop *iop_p;
|
|
Packit |
d36e9b |
|
|
Packit |
d36e9b |
iop_p = (iop *)ddi_get_soft_state(state_head, getminor(dev));
|
|
Packit |
d36e9b |
return 0;
|
|
Packit |
d36e9b |
}
|
|
Packit |
d36e9b |
|
|
Packit |
d36e9b |
/* cmd should be IOPREAD or IOPWRITE
|
|
Packit |
d36e9b |
arg is the address (of the io port)
|
|
Packit |
d36e9b |
*/
|
|
Packit |
d36e9b |
int
|
|
Packit |
d36e9b |
iopioctl(dev_t dev,
|
|
Packit |
d36e9b |
int cmd,
|
|
Packit |
d36e9b |
intptr_t arg,
|
|
Packit |
d36e9b |
int mode,
|
|
Packit |
d36e9b |
cred_t *credp,
|
|
Packit |
d36e9b |
int *rvalp)
|
|
Packit |
d36e9b |
{
|
|
Packit |
d36e9b |
iop *iop_p;
|
|
Packit |
d36e9b |
int retval = 0;
|
|
Packit |
d36e9b |
iopbuf tmpbuf;
|
|
Packit |
d36e9b |
|
|
Packit |
d36e9b |
iop_p = (iop *)ddi_get_soft_state(state_head, getminor(dev));
|
|
Packit |
d36e9b |
|
|
Packit |
d36e9b |
/* we will have to check privileges once we do writes */
|
|
Packit |
d36e9b |
switch (cmd) {
|
|
Packit |
d36e9b |
case IOPREAD:
|
|
Packit |
d36e9b |
mutex_enter(&iop_p->mutex);
|
|
Packit |
d36e9b |
if (ddi_copyin((caddr_t)arg,
|
|
Packit |
d36e9b |
(caddr_t)&tmpbuf,
|
|
Packit |
d36e9b |
sizeof(tmpbuf),
|
|
Packit |
d36e9b |
mode))
|
|
Packit |
d36e9b |
{
|
|
Packit |
d36e9b |
retval = EFAULT;
|
|
Packit |
d36e9b |
} else {
|
|
Packit |
d36e9b |
tmpbuf.port_value = inb(tmpbuf.port);
|
|
Packit |
d36e9b |
ddi_copyout((caddr_t)&tmpbuf.port_value,
|
|
Packit |
d36e9b |
(caddr_t)(arg+sizeof(tmpbuf.port)),
|
|
Packit |
d36e9b |
sizeof(tmpbuf.port),
|
|
Packit |
d36e9b |
mode);
|
|
Packit |
d36e9b |
}
|
|
Packit |
d36e9b |
mutex_exit(&iop_p->mutex);
|
|
Packit |
d36e9b |
break;
|
|
Packit |
d36e9b |
case IOPWRITE:
|
|
Packit |
d36e9b |
mutex_enter(&iop_p->mutex);
|
|
Packit |
d36e9b |
if (ddi_copyin((caddr_t)arg,
|
|
Packit |
d36e9b |
(caddr_t)&tmpbuf,
|
|
Packit |
d36e9b |
sizeof(tmpbuf),
|
|
Packit |
d36e9b |
mode))
|
|
Packit |
d36e9b |
{
|
|
Packit |
d36e9b |
retval = EFAULT;
|
|
Packit |
d36e9b |
} else {
|
|
Packit |
d36e9b |
outb(tmpbuf.port,tmpbuf.port_value);
|
|
Packit |
d36e9b |
}
|
|
Packit |
d36e9b |
mutex_exit(&iop_p->mutex);
|
|
Packit |
d36e9b |
break;
|
|
Packit |
d36e9b |
default:
|
|
Packit |
d36e9b |
retval = EINVAL;
|
|
Packit |
d36e9b |
break;
|
|
Packit |
d36e9b |
}
|
|
Packit |
d36e9b |
return retval;
|
|
Packit |
d36e9b |
|
|
Packit |
d36e9b |
}
|