xref: /illumos-gate/usr/src/uts/intel/io/vgatext/vgatext.c (revision 8704b322)
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
_init(void)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
_fini(void)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
_info(struct modinfo * modinfop)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
vgatext_attach(dev_info_t * devi,ddi_attach_cmd_t cmd)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, 0);
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
vgatext_detach(dev_info_t * devi,ddi_detach_cmd_t cmd)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
vgatext_info(dev_info_t * dip,ddi_info_cmd_t infocmd,void * arg,void ** result)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
vgatext_open(dev_t * devp,int flag,int otyp,cred_t * cred)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
vgatext_close(dev_t devp,int flag,int otyp,cred_t * cred)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
vgatext_ioctl(dev_t dev,int cmd,intptr_t data,int mode,cred_t * cred,int * rval)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
vgatext_devmap(dev_t dev,devmap_cookie_t dhp,offset_t off,size_t len,size_t * maplen,uint_t model)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