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