1 /*
   2  * CDDL HEADER START
   3  *
   4  * The contents of this file are subject to the terms of the
   5  * Common Development and Distribution License (the "License").
   6  * You may not use this file except in compliance with the License.
   7  *
   8  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
   9  * or http://www.opensolaris.org/os/licensing.
  10  * See the License for the specific language governing permissions
  11  * and limitations under the License.
  12  *
  13  * When distributing Covered Code, include this CDDL HEADER in each
  14  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
  15  * If applicable, add the following below this CDDL HEADER, with the
  16  * fields enclosed by brackets "[]" replaced with your own identifying
  17  * information: Portions Copyright [yyyy] [name of copyright owner]
  18  *
  19  * CDDL HEADER END
  20  */
  21 
  22 /*
  23  * Copyright 2010 Sun Microsystems, Inc.  All rights reserved.
  24  * Use is subject to license terms.
  25  */
  26 
  27 /*      Copyright (c) 1990, 1991 UNIX System Laboratories, Inc. */
  28 /*      Copyright (c) 1984, 1986, 1987, 1988, 1989, 1990 AT&T       */
  29 /*        All Rights Reserved   */
  30 
  31 #include <sys/errno.h>
  32 #include <sys/types.h>
  33 #include <sys/conf.h>
  34 #include <sys/kmem.h>
  35 #include <sys/visual_io.h>
  36 #include <sys/font.h>
  37 #include <sys/fbio.h>
  38 
  39 #include <sys/ddi.h>
  40 #include <sys/stat.h>
  41 #include <sys/sunddi.h>
  42 #include <sys/file.h>
  43 #include <sys/open.h>
  44 #include <sys/modctl.h>
  45 #include <sys/vgareg.h>
  46 #include <sys/vgasubr.h>
  47 #include <sys/pci.h>
  48 #include <sys/kd.h>
  49 #include <sys/ddi_impldefs.h>
  50 #include <sys/sunldi.h>
  51 #include <sys/gfx_private.h>
  52 
  53 #define MYNAME  "vgatext"
  54 
  55 /*
  56  * Each instance of this driver has 2 minor nodes:
  57  * 0: for common graphics operations
  58  * 1: for agpmaster operations
  59  */
  60 #define GFX_MINOR               0
  61 #define AGPMASTER_MINOR         1
  62 
  63 #define MY_NBITSMINOR           1
  64 #define DEV2INST(dev)           (getminor(dev) >> MY_NBITSMINOR)
  65 #define DEV2MINOR(dev)          (getminor(dev) & ((1 << MY_NBITSMINOR) - 1))
  66 #define INST2NODE1(inst)        (((inst) << MY_NBITSMINOR) + GFX_MINOR)
  67 #define INST2NODE2(inst)        (((inst) << MY_NBITSMINOR) + AGPMASTER_MINOR)
  68 
  69 /*
  70  * This variable allows for this driver to suspend even if it
  71  * shouldn't.  Note that by setting it, the framebuffer will probably
  72  * not come back.  So use it with a serial console, or with serial
  73  * line debugging (say, for example, if this driver is being modified
  74  * to support _some_ hardware doing suspend and resume).
  75  */
  76 int vgatext_force_suspend = 0;
  77 
  78 static int vgatext_open(dev_t *, int, int, cred_t *);
  79 static int vgatext_close(dev_t, int, int, cred_t *);
  80 static int vgatext_ioctl(dev_t, int, intptr_t, int, cred_t *, int *);
  81 static int vgatext_devmap(dev_t, devmap_cookie_t, offset_t, size_t,
  82                             size_t *, uint_t);
  83 
  84 static  struct cb_ops cb_vgatext_ops = {
  85         vgatext_open,           /* cb_open */
  86         vgatext_close,          /* cb_close */
  87         nodev,                  /* cb_strategy */
  88         nodev,                  /* cb_print */
  89         nodev,                  /* cb_dump */
  90         nodev,                  /* cb_read */
  91         nodev,                  /* cb_write */
  92         vgatext_ioctl,          /* cb_ioctl */
  93         vgatext_devmap,         /* cb_devmap */
  94         nodev,                  /* cb_mmap */
  95         ddi_devmap_segmap,      /* cb_segmap */
  96         nochpoll,               /* cb_chpoll */
  97         ddi_prop_op,            /* cb_prop_op */
  98         0,                      /* cb_stream */
  99         D_NEW | D_MTSAFE        /* cb_flag */
 100 };
 101 
 102 static int vgatext_info(dev_info_t *dip, ddi_info_cmd_t infocmd, void *arg,
 103                 void **result);
 104 static int vgatext_attach(dev_info_t *, ddi_attach_cmd_t);
 105 static int vgatext_detach(dev_info_t *, ddi_detach_cmd_t);
 106 
 107 static struct dev_ops vgatext_ops = {
 108         DEVO_REV,               /* devo_rev */
 109         0,                      /* devo_refcnt */
 110         vgatext_info,           /* devo_getinfo */
 111         nulldev,                /* devo_identify */
 112         nulldev,                /* devo_probe */
 113         vgatext_attach,         /* devo_attach */
 114         vgatext_detach,         /* devo_detach */
 115         nodev,                  /* devo_reset */
 116         &cb_vgatext_ops,    /* devo_cb_ops */
 117         (struct bus_ops *)NULL, /* devo_bus_ops */
 118         NULL,                   /* power */
 119         ddi_quiesce_not_needed, /* quiesce */
 120 };
 121 
 122 struct vgatext_softc {
 123         gfxp_fb_softc_ptr_t gfxp_state;
 124         dev_info_t              *devi;
 125 };
 126 
 127 static void     *vgatext_softc_head;
 128 
 129 /* Loadable Driver stuff */
 130 
 131 static struct modldrv modldrv = {
 132         &mod_driverops,             /* Type of module.  This one is a driver */
 133         "VGA text driver",      /* Name of the module. */
 134         &vgatext_ops,               /* driver ops */
 135 };
 136 
 137 static struct modlinkage modlinkage = {
 138         MODREV_1, (void *) &modldrv, NULL
 139 };
 140 
 141 int
 142 _init(void)
 143 {
 144         int e;
 145 
 146         if ((e = ddi_soft_state_init(&vgatext_softc_head,
 147                     sizeof (struct vgatext_softc), 1)) != 0) {
 148             return (e);
 149         }
 150 
 151         e = mod_install(&modlinkage);
 152 
 153         if (e) {
 154                 ddi_soft_state_fini(&vgatext_softc_head);
 155         }
 156         return (e);
 157 }
 158 
 159 int
 160 _fini(void)
 161 {
 162         int e;
 163 
 164         if ((e = mod_remove(&modlinkage)) != 0)
 165                 return (e);
 166 
 167         ddi_soft_state_fini(&vgatext_softc_head);
 168 
 169         return (0);
 170 }
 171 
 172 int
 173 _info(struct modinfo *modinfop)
 174 {
 175         return (mod_info(&modlinkage, modinfop));
 176 }
 177 
 178 /*
 179  * handy macros
 180  */
 181 
 182 #define getsoftc(instance) ((struct vgatext_softc *)    \
 183                         ddi_get_soft_state(vgatext_softc_head, (instance)))
 184 
 185 #define STREQ(a, b)     (strcmp((a), (b)) == 0)
 186 
 187 static int
 188 vgatext_attach(dev_info_t *devi, ddi_attach_cmd_t cmd)
 189 {
 190         struct vgatext_softc *softc;
 191         int     unit = ddi_get_instance(devi);
 192         int     error;
 193         char    name[80];
 194 
 195 
 196         switch (cmd) {
 197         case DDI_ATTACH:
 198                 break;
 199 
 200         case DDI_RESUME:
 201                 /*
 202                  * Though vgatext doesn't really know how to resume
 203                  * on a generic framebuffer, we should succeed, as
 204                  * it is far better to have no console, than potentiall
 205                  * have no machine.
 206                  */
 207                 softc = getsoftc(unit);
 208                 return (gfxp_fb_attach(devi, cmd, softc->gfxp_state));
 209         default:
 210                 return (DDI_FAILURE);
 211         }
 212 
 213         /* DDI_ATTACH */
 214 
 215         /* Allocate softc struct */
 216         if (ddi_soft_state_zalloc(vgatext_softc_head, unit) != DDI_SUCCESS) {
 217                 return (DDI_FAILURE);
 218         }
 219         softc = getsoftc(unit);
 220         softc->gfxp_state = gfxp_fb_softc_alloc();
 221         if (softc->gfxp_state == NULL) {
 222                 (void) ddi_soft_state_free(vgatext_softc_head, unit);
 223                 return (DDI_FAILURE);
 224         }
 225 
 226         if (gfxp_fb_attach(devi, cmd, softc->gfxp_state) != DDI_SUCCESS) {
 227                 gfxp_fb_softc_free(softc->gfxp_state);
 228                 (void) ddi_soft_state_free(vgatext_softc_head, unit);
 229                 return (DDI_FAILURE);
 230         }
 231 
 232         /* link it in */
 233         softc->devi = devi;
 234         ddi_set_driver_private(devi, softc);
 235 
 236         (void) snprintf(name, sizeof (name), "text-%d", unit);
 237         error = ddi_create_minor_node(devi, name, S_IFCHR,
 238             INST2NODE1(unit), DDI_NT_DISPLAY, NULL);
 239         if (error == DDI_SUCCESS)
 240                 return (DDI_SUCCESS);
 241 
 242         (void) vgatext_detach(devi, DDI_DETACH);
 243         return (error);
 244 }
 245 
 246 static int
 247 vgatext_detach(dev_info_t *devi, ddi_detach_cmd_t cmd)
 248 {
 249         int instance = ddi_get_instance(devi);
 250         struct vgatext_softc *softc = getsoftc(instance);
 251 
 252 
 253         switch (cmd) {
 254         case DDI_DETACH:
 255                 (void) gfxp_fb_detach(devi, cmd, softc->gfxp_state);
 256 
 257                 if (softc->gfxp_state != NULL)
 258                         gfxp_fb_softc_free(softc->gfxp_state);
 259                 ddi_remove_minor_node(devi, NULL);
 260                 (void) ddi_soft_state_free(vgatext_softc_head, instance);
 261                 return (DDI_SUCCESS);
 262 
 263         case DDI_SUSPEND:
 264                 /*
 265                  * This is a generic VGA file, and therefore, cannot
 266                  * understand how to deal with suspend and resume on
 267                  * a generic interface.  So we fail any attempt to
 268                  * suspend.  At some point in the future, we might use
 269                  * this as an entrypoint for display drivers and this
 270                  * assumption may change.
 271                  *
 272                  * However, from a platform development perspective,
 273                  * it is important that this driver suspend if a
 274                  * developer is using a serial console and/or working
 275                  * on a framebuffer driver that will support suspend
 276                  * and resume.  Therefore, we have this module tunable
 277                  * (purposely using a long name) that will allow for
 278                  * suspend it it is set.  Otherwise we fail.
 279                  */
 280                 if (vgatext_force_suspend != 0)
 281                         return (gfxp_fb_detach(devi, cmd, softc->gfxp_state));
 282                 else
 283                         return (DDI_FAILURE);
 284 
 285         default:
 286                 cmn_err(CE_WARN, "vgatext_detach: unknown cmd 0x%x\n", cmd);
 287                 return (DDI_FAILURE);
 288         }
 289 }
 290 
 291 /*ARGSUSED*/
 292 static int
 293 vgatext_info(dev_info_t *dip, ddi_info_cmd_t infocmd, void *arg, void **result)
 294 {
 295         dev_t dev;
 296         int error;
 297         int instance;
 298         struct vgatext_softc *softc;
 299 
 300         error = DDI_SUCCESS;
 301 
 302         dev = (dev_t)arg;
 303         instance = DEV2INST(dev);
 304         softc = getsoftc(instance);
 305 
 306         switch (infocmd) {
 307         case DDI_INFO_DEVT2DEVINFO:
 308                 if (softc == NULL || softc->devi == NULL) {
 309                         error = DDI_FAILURE;
 310                 } else {
 311                         *result = (void *) softc->devi;
 312                         error = DDI_SUCCESS;
 313                 }
 314                 break;
 315         case DDI_INFO_DEVT2INSTANCE:
 316                 *result = (void *)(uintptr_t)instance;
 317                 error = DDI_SUCCESS;
 318                 break;
 319         default:
 320                 error = DDI_FAILURE;
 321                 break;
 322         }
 323         return (error);
 324 }
 325 
 326 
 327 static int
 328 vgatext_open(dev_t *devp, int flag, int otyp, cred_t *cred)
 329 {
 330         struct vgatext_softc *softc = getsoftc(DEV2INST(*devp));
 331 
 332         if (softc == NULL)
 333                 return (ENXIO);
 334 
 335         return (gfxp_fb_open(devp, flag, otyp, cred, softc->gfxp_state));
 336 }
 337 
 338 static int
 339 vgatext_close(dev_t devp, int flag, int otyp, cred_t *cred)
 340 {
 341         struct vgatext_softc *softc = getsoftc(DEV2INST(devp));
 342 
 343         if (softc == NULL)
 344                 return (ENXIO);
 345 
 346         return (gfxp_fb_close(devp, flag, otyp, cred, softc->gfxp_state));
 347 }
 348 
 349 static int
 350 vgatext_ioctl(
 351     dev_t dev,
 352     int cmd,
 353     intptr_t data,
 354     int mode,
 355     cred_t *cred,
 356     int *rval)
 357 {
 358         struct vgatext_softc *softc = getsoftc(DEV2INST(dev));
 359         int err;
 360 
 361         switch (DEV2MINOR(dev)) {
 362         case GFX_MINOR:
 363                 err = gfxp_fb_ioctl(dev, cmd, data, mode, cred, rval,
 364                     softc->gfxp_state);
 365                 break;
 366 
 367         case AGPMASTER_MINOR:
 368                 /*
 369                  * This is apparently not used anymore.  Let's log a
 370                  * message so we'll know if some consumer shows up.
 371                  * If it turns out that we actually do need to keep
 372                  * support for this pass-through to agpmaster, it
 373                  * would probably be better to use "layered" access
 374                  * to the AGP device (ldi_open, ldi_ioctl, ldi_close)
 375                  */
 376                 cmn_err(CE_NOTE, "!vgatext wants agpmaster");
 377                 return (EBADF);
 378 
 379         default:
 380                 /* not a valid minor node */
 381                 return (EBADF);
 382         }
 383         return (err);
 384 }
 385 
 386 static int
 387 vgatext_devmap(dev_t dev, devmap_cookie_t dhp, offset_t off, size_t len,
 388     size_t *maplen, uint_t model)
 389 {
 390         struct vgatext_softc *softc;
 391 
 392         softc = getsoftc(DEV2INST(dev));
 393         if (softc == NULL) {
 394                 cmn_err(CE_WARN, "vgatext: Can't find softstate");
 395                 return (-1);
 396         }
 397 
 398         return (gfxp_fb_devmap(dev, dhp, off, len, maplen, model,
 399             softc->gfxp_state));
 400 }