fbd.c revision 271117
1/*- 2 * Copyright (c) 2013 The FreeBSD Foundation 3 * All rights reserved. 4 * 5 * This software was developed by Aleksandr Rybalko under sponsorship from the 6 * FreeBSD Foundation. 7 * 8 * Redistribution and use in source and binary forms, with or without 9 * modification, are permitted provided that the following conditions 10 * are met: 11 * 1. Redistributions of source code must retain the above copyright 12 * notice, this list of conditions and the following disclaimer. 13 * 2. Redistributions in binary form must reproduce the above copyright 14 * notice, this list of conditions and the following disclaimer in the 15 * documentation and/or other materials provided with the distribution. 16 * 17 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 18 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 19 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 20 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 21 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 22 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 23 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 24 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 25 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 26 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 27 * SUCH DAMAGE. 28 * 29 * $FreeBSD: stable/10/sys/dev/fb/fbd.c 271117 2014-09-04 18:43:40Z emaste $ 30 */ 31 32/* Generic framebuffer */ 33/* TODO unlink from VT(9) */ 34/* TODO done normal /dev/fb methods */ 35 36#include <sys/cdefs.h> 37__FBSDID("$FreeBSD: stable/10/sys/dev/fb/fbd.c 271117 2014-09-04 18:43:40Z emaste $"); 38 39#include <sys/param.h> 40#include <sys/systm.h> 41#include <sys/bus.h> 42#include <sys/conf.h> 43#include <sys/kernel.h> 44#include <sys/malloc.h> 45#include <sys/module.h> 46#include <sys/queue.h> 47#include <sys/fbio.h> 48 49#include <machine/bus.h> 50 51#include <dev/vt/vt.h> 52#include <dev/vt/hw/fb/vt_fb.h> 53 54#include "fb_if.h" 55 56LIST_HEAD(fb_list_head_t, fb_list_entry) fb_list_head = 57 LIST_HEAD_INITIALIZER(fb_list_head); 58struct fb_list_entry { 59 struct fb_info *fb_info; 60 struct cdev *fb_si; 61 LIST_ENTRY(fb_list_entry) fb_list; 62}; 63 64struct fbd_softc { 65 device_t sc_dev; 66 struct fb_info *sc_info; 67}; 68 69static void fbd_evh_init(void *); 70/* SI_ORDER_SECOND, just after EVENTHANDLERs initialized. */ 71SYSINIT(fbd_evh_init, SI_SUB_CONFIGURE, SI_ORDER_SECOND, fbd_evh_init, NULL); 72 73static d_open_t fb_open; 74static d_close_t fb_close; 75static d_read_t fb_read; 76static d_write_t fb_write; 77static d_ioctl_t fb_ioctl; 78static d_mmap_t fb_mmap; 79 80static struct cdevsw fb_cdevsw = { 81 .d_version = D_VERSION, 82 .d_flags = D_NEEDGIANT, 83 .d_open = fb_open, 84 .d_close = fb_close, 85 .d_read = fb_read, 86 .d_write = fb_write, 87 .d_ioctl = fb_ioctl, 88 .d_mmap = fb_mmap, 89 .d_name = "fb", 90}; 91 92static int framebuffer_dev_unit = 0; 93 94static int 95fb_open(struct cdev *dev, int oflags, int devtype, struct thread *td) 96{ 97 98 return (0); 99} 100 101static int 102fb_close(struct cdev *dev, int fflag, int devtype, struct thread *td) 103{ 104 105 return (0); 106} 107 108static int 109fb_ioctl(struct cdev *dev, u_long cmd, caddr_t data, int fflag, 110 struct thread *td) 111{ 112 struct fb_info *info; 113 int error; 114 115 error = 0; 116 info = dev->si_drv1; 117 118 switch (cmd) { 119 case FBIOGTYPE: 120 bcopy(info, (struct fbtype *)data, sizeof(struct fbtype)); 121 break; 122 123 case FBIO_GETWINORG: /* get frame buffer window origin */ 124 *(u_int *)data = 0; 125 break; 126 127 case FBIO_GETDISPSTART: /* get display start address */ 128 ((video_display_start_t *)data)->x = 0; 129 ((video_display_start_t *)data)->y = 0; 130 break; 131 132 case FBIO_GETLINEWIDTH: /* get scan line width in bytes */ 133 *(u_int *)data = info->fb_stride; 134 break; 135 136 case FBIO_BLANK: /* blank display */ 137 error = 0; /* TODO */ 138 break; 139 140 default: 141 error = ENOIOCTL; 142 break; 143 } 144 return (error); 145} 146 147static int 148fb_read(struct cdev *dev, struct uio *uio, int ioflag) 149{ 150 151 return (0); /* XXX nothing to read, yet */ 152} 153 154static int 155fb_write(struct cdev *dev, struct uio *uio, int ioflag) 156{ 157 158 return (0); /* XXX nothing written */ 159} 160 161static int 162fb_mmap(struct cdev *dev, vm_ooffset_t offset, vm_paddr_t *paddr, int nprot, 163 vm_memattr_t *memattr) 164{ 165 struct fb_info *info; 166 167 info = dev->si_drv1; 168 169 if ((info->fb_flags & FB_FLAG_NOMMAP) || info->fb_pbase == 0) 170 return (ENODEV); 171 172 if (offset < info->fb_size) { 173 *paddr = info->fb_pbase + offset; 174 return (0); 175 } 176 return (EINVAL); 177} 178 179static int 180fb_init(struct fb_list_entry *entry, int unit) 181{ 182 struct fb_info *info; 183 184 info = entry->fb_info; 185 entry->fb_si = make_dev(&fb_cdevsw, unit, UID_ROOT, GID_WHEEL, 186 0600, "fb%d", unit); 187 entry->fb_si->si_drv1 = info; 188 info->fb_cdev = entry->fb_si; 189 190 return (0); 191} 192 193int 194fbd_list() 195{ 196 struct fb_list_entry *entry; 197 198 if (LIST_EMPTY(&fb_list_head)) 199 return (ENOENT); 200 201 LIST_FOREACH(entry, &fb_list_head, fb_list) { 202 printf("FB %s @%p\n", entry->fb_info->fb_name, 203 (void *)entry->fb_info->fb_pbase); 204 } 205 206 return (0); 207} 208 209static struct fb_list_entry * 210fbd_find(struct fb_info* info) 211{ 212 struct fb_list_entry *entry, *tmp; 213 214 LIST_FOREACH_SAFE(entry, &fb_list_head, fb_list, tmp) { 215 if (entry->fb_info == info) { 216 return (entry); 217 } 218 } 219 220 return (NULL); 221} 222 223int 224fbd_register(struct fb_info* info) 225{ 226 struct fb_list_entry *entry; 227 int err, first; 228 229 first = 0; 230 if (LIST_EMPTY(&fb_list_head)) 231 first++; 232 233 entry = fbd_find(info); 234 if (entry != NULL) { 235 /* XXX Update framebuffer params */ 236 return (0); 237 } 238 239 entry = malloc(sizeof(struct fb_list_entry), M_DEVBUF, M_WAITOK|M_ZERO); 240 entry->fb_info = info; 241 242 LIST_INSERT_HEAD(&fb_list_head, entry, fb_list); 243 244 err = fb_init(entry, framebuffer_dev_unit++); 245 if (err) 246 return (err); 247 248 if (first) { 249 if (vt_fb_attach(info) == CN_DEAD) 250 return (ENXIO); 251 } 252 253 return (0); 254} 255 256int 257fbd_unregister(struct fb_info* info) 258{ 259 struct fb_list_entry *entry, *tmp; 260 261 LIST_FOREACH_SAFE(entry, &fb_list_head, fb_list, tmp) { 262 if (entry->fb_info == info) { 263 LIST_REMOVE(entry, fb_list); 264 free(entry, M_DEVBUF); 265 return (0); 266 } 267 } 268 269 return (ENOENT); 270} 271 272static void 273register_fb_wrap(void *arg, void *ptr) 274{ 275 276 fbd_register((struct fb_info *)ptr); 277} 278 279static void 280unregister_fb_wrap(void *arg, void *ptr) 281{ 282 283 fbd_unregister((struct fb_info *)ptr); 284} 285 286static void 287fbd_evh_init(void *ctx) 288{ 289 290 EVENTHANDLER_REGISTER(register_framebuffer, register_fb_wrap, NULL, 291 EVENTHANDLER_PRI_ANY); 292 EVENTHANDLER_REGISTER(unregister_framebuffer, unregister_fb_wrap, NULL, 293 EVENTHANDLER_PRI_ANY); 294} 295 296/* Newbus methods. */ 297static int 298fbd_probe(device_t dev) 299{ 300 301 return (BUS_PROBE_NOWILDCARD); 302} 303 304static int 305fbd_attach(device_t dev) 306{ 307 struct fbd_softc *sc; 308 int err; 309 310 sc = device_get_softc(dev); 311 312 sc->sc_dev = dev; 313 sc->sc_info = FB_GETINFO(device_get_parent(dev)); 314 if (sc->sc_info == NULL) 315 return (ENXIO); 316 err = fbd_register(sc->sc_info); 317 318 return (err); 319} 320 321static int 322fbd_detach(device_t dev) 323{ 324 struct fbd_softc *sc; 325 int err; 326 327 sc = device_get_softc(dev); 328 329 err = fbd_unregister(sc->sc_info); 330 331 return (err); 332} 333 334static int 335fbd_suspend(device_t dev) 336{ 337 338 vt_fb_suspend(); 339 return (bus_generic_suspend(dev)); 340} 341 342static int 343fbd_resume(device_t dev) 344{ 345 346 vt_fb_resume(); 347 return (bus_generic_resume(dev)); 348} 349 350static device_method_t fbd_methods[] = { 351 /* Device interface */ 352 DEVMETHOD(device_probe, fbd_probe), 353 DEVMETHOD(device_attach, fbd_attach), 354 DEVMETHOD(device_detach, fbd_detach), 355 356 DEVMETHOD(device_shutdown, bus_generic_shutdown), 357 DEVMETHOD(device_suspend, fbd_suspend), 358 DEVMETHOD(device_resume, fbd_resume), 359 360 { 0, 0 } 361}; 362 363driver_t fbd_driver = { 364 "fbd", 365 fbd_methods, 366 sizeof(struct fbd_softc) 367}; 368 369devclass_t fbd_devclass; 370 371DRIVER_MODULE(fbd, fb, fbd_driver, fbd_devclass, 0, 0); 372DRIVER_MODULE(fbd, drmn, fbd_driver, fbd_devclass, 0, 0); 373MODULE_VERSION(fbd, 1); 374 375