#include <sys/errno.h>
#include <sys/types.h>
#include <sys/conf.h>
#include <sys/kmem.h>
#include <sys/visual_io.h>
#include <sys/font.h>
#include <sys/fbio.h>
#include <sys/ddi.h>
#include <sys/stat.h>
#include <sys/sunddi.h>
#include <sys/file.h>
#include <sys/open.h>
#include <sys/modctl.h>
#include <sys/vgareg.h>
#include <sys/vgasubr.h>
#include <sys/pci.h>
#include <sys/kd.h>
#include <sys/ddi_impldefs.h>
#include <sys/sunldi.h>
#include <sys/gfx_private.h>
#define MYNAME "vgatext"
#define GFX_MINOR 0
#define AGPMASTER_MINOR 1
#define MY_NBITSMINOR 1
#define DEV2INST(dev) (getminor(dev) >> MY_NBITSMINOR)
#define DEV2MINOR(dev) (getminor(dev) & ((1 << MY_NBITSMINOR) - 1))
#define INST2NODE1(inst) (((inst) << MY_NBITSMINOR) + GFX_MINOR)
#define INST2NODE2(inst) (((inst) << MY_NBITSMINOR) + AGPMASTER_MINOR)
int vgatext_force_suspend = 0;
static int vgatext_open(dev_t *, int, int, cred_t *);
static int vgatext_close(dev_t, int, int, cred_t *);
static int vgatext_ioctl(dev_t, int, intptr_t, int, cred_t *, int *);
static int vgatext_devmap(dev_t, devmap_cookie_t, offset_t, size_t,
size_t *, uint_t);
static struct cb_ops cb_vgatext_ops = {
vgatext_open,
vgatext_close,
nodev,
nodev,
nodev,
nodev,
nodev,
vgatext_ioctl,
vgatext_devmap,
nodev,
ddi_devmap_segmap,
nochpoll,
ddi_prop_op,
0,
D_NEW | D_MTSAFE
};
static int vgatext_info(dev_info_t *dip, ddi_info_cmd_t infocmd, void *arg,
void **result);
static int vgatext_attach(dev_info_t *, ddi_attach_cmd_t);
static int vgatext_detach(dev_info_t *, ddi_detach_cmd_t);
static struct dev_ops vgatext_ops = {
DEVO_REV,
0,
vgatext_info,
nulldev,
nulldev,
vgatext_attach,
vgatext_detach,
nodev,
&cb_vgatext_ops,
(struct bus_ops *)NULL,
NULL,
ddi_quiesce_not_needed,
};
struct vgatext_softc {
gfxp_fb_softc_ptr_t gfxp_state;
dev_info_t *devi;
};
static void *vgatext_softc_head;
static struct modldrv modldrv = {
&mod_driverops,
"VGA text driver",
&vgatext_ops,
};
static struct modlinkage modlinkage = {
MODREV_1, (void *) &modldrv, NULL
};
int
_init(void)
{
int e;
if ((e = ddi_soft_state_init(&vgatext_softc_head,
sizeof (struct vgatext_softc), 1)) != 0) {
return (e);
}
e = mod_install(&modlinkage);
if (e) {
ddi_soft_state_fini(&vgatext_softc_head);
}
return (e);
}
int
_fini(void)
{
int e;
if ((e = mod_remove(&modlinkage)) != 0)
return (e);
ddi_soft_state_fini(&vgatext_softc_head);
return (0);
}
int
_info(struct modinfo *modinfop)
{
return (mod_info(&modlinkage, modinfop));
}
#define getsoftc(instance) ((struct vgatext_softc *) \
ddi_get_soft_state(vgatext_softc_head, (instance)))
#define STREQ(a, b) (strcmp((a), (b)) == 0)
static int
vgatext_attach(dev_info_t *devi, ddi_attach_cmd_t cmd)
{
struct vgatext_softc *softc;
int unit = ddi_get_instance(devi);
int error;
char name[80];
switch (cmd) {
case DDI_ATTACH:
break;
case DDI_RESUME:
softc = getsoftc(unit);
return (gfxp_fb_attach(devi, cmd, softc->gfxp_state));
default:
return (DDI_FAILURE);
}
if (ddi_soft_state_zalloc(vgatext_softc_head, unit) != DDI_SUCCESS) {
return (DDI_FAILURE);
}
softc = getsoftc(unit);
softc->gfxp_state = gfxp_fb_softc_alloc();
if (softc->gfxp_state == NULL) {
(void) ddi_soft_state_free(vgatext_softc_head, unit);
return (DDI_FAILURE);
}
if (gfxp_fb_attach(devi, cmd, softc->gfxp_state) != DDI_SUCCESS) {
gfxp_fb_softc_free(softc->gfxp_state);
(void) ddi_soft_state_free(vgatext_softc_head, unit);
return (DDI_FAILURE);
}
softc->devi = devi;
ddi_set_driver_private(devi, softc);
(void) snprintf(name, sizeof (name), "text-%d", unit);
error = ddi_create_minor_node(devi, name, S_IFCHR,
INST2NODE1(unit), DDI_NT_DISPLAY, 0);
if (error == DDI_SUCCESS)
return (DDI_SUCCESS);
(void) vgatext_detach(devi, DDI_DETACH);
return (error);
}
static int
vgatext_detach(dev_info_t *devi, ddi_detach_cmd_t cmd)
{
int instance = ddi_get_instance(devi);
struct vgatext_softc *softc = getsoftc(instance);
switch (cmd) {
case DDI_DETACH:
(void) gfxp_fb_detach(devi, cmd, softc->gfxp_state);
if (softc->gfxp_state != NULL)
gfxp_fb_softc_free(softc->gfxp_state);
ddi_remove_minor_node(devi, NULL);
(void) ddi_soft_state_free(vgatext_softc_head, instance);
return (DDI_SUCCESS);
case DDI_SUSPEND:
if (vgatext_force_suspend != 0)
return (gfxp_fb_detach(devi, cmd, softc->gfxp_state));
else
return (DDI_FAILURE);
default:
cmn_err(CE_WARN, "vgatext_detach: unknown cmd 0x%x\n", cmd);
return (DDI_FAILURE);
}
}
static int
vgatext_info(dev_info_t *dip, ddi_info_cmd_t infocmd, void *arg, void **result)
{
dev_t dev;
int error;
int instance;
struct vgatext_softc *softc;
error = DDI_SUCCESS;
dev = (dev_t)arg;
instance = DEV2INST(dev);
softc = getsoftc(instance);
switch (infocmd) {
case DDI_INFO_DEVT2DEVINFO:
if (softc == NULL || softc->devi == NULL) {
error = DDI_FAILURE;
} else {
*result = (void *) softc->devi;
error = DDI_SUCCESS;
}
break;
case DDI_INFO_DEVT2INSTANCE:
*result = (void *)(uintptr_t)instance;
error = DDI_SUCCESS;
break;
default:
error = DDI_FAILURE;
break;
}
return (error);
}
static int
vgatext_open(dev_t *devp, int flag, int otyp, cred_t *cred)
{
struct vgatext_softc *softc = getsoftc(DEV2INST(*devp));
if (softc == NULL)
return (ENXIO);
return (gfxp_fb_open(devp, flag, otyp, cred, softc->gfxp_state));
}
static int
vgatext_close(dev_t devp, int flag, int otyp, cred_t *cred)
{
struct vgatext_softc *softc = getsoftc(DEV2INST(devp));
if (softc == NULL)
return (ENXIO);
return (gfxp_fb_close(devp, flag, otyp, cred, softc->gfxp_state));
}
static int
vgatext_ioctl(
dev_t dev,
int cmd,
intptr_t data,
int mode,
cred_t *cred,
int *rval)
{
struct vgatext_softc *softc = getsoftc(DEV2INST(dev));
int err;
switch (DEV2MINOR(dev)) {
case GFX_MINOR:
err = gfxp_fb_ioctl(dev, cmd, data, mode, cred, rval,
softc->gfxp_state);
break;
case AGPMASTER_MINOR:
cmn_err(CE_NOTE, "!vgatext wants agpmaster");
return (EBADF);
default:
return (EBADF);
}
return (err);
}
static int
vgatext_devmap(dev_t dev, devmap_cookie_t dhp, offset_t off, size_t len,
size_t *maplen, uint_t model)
{
struct vgatext_softc *softc;
softc = getsoftc(DEV2INST(dev));
if (softc == NULL) {
cmn_err(CE_WARN, "vgatext: Can't find softstate");
return (-1);
}
return (gfxp_fb_devmap(dev, dhp, off, len, maplen, model,
softc->gfxp_state));
}