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 } 401