fbd.c revision 256905
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: user/ed/newcons/sys/dev/fb/fbd.c 256905 2013-10-22 14:45:35Z ray $ 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: user/ed/newcons/sys/dev/fb/fbd.c 256905 2013-10-22 14:45:35Z ray $"); 38 39#include <sys/param.h> 40#include <sys/systm.h> 41#include <sys/conf.h> 42#include <sys/kernel.h> 43#include <sys/malloc.h> 44#include <sys/queue.h> 45#include <sys/fbio.h> 46#include <dev/vt/hw/fb/vt_fb.h> 47 48LIST_HEAD(fb_list_head_t, fb_list_entry) fb_list_head = 49 LIST_HEAD_INITIALIZER(fb_list_head); 50struct fb_list_entry { 51 struct fb_info *fb_info; 52 struct cdev *fb_si; 53 LIST_ENTRY(fb_list_entry) fb_list; 54}; 55 56static void fbd_evh_init(void *); 57/* SI_ORDER_SECOND, just after EVENTHANDLERs initialized. */ 58SYSINIT(fbd_evh_init, SI_SUB_CONFIGURE, SI_ORDER_SECOND, fbd_evh_init, NULL); 59 60static d_open_t fb_open; 61static d_close_t fb_close; 62static d_read_t fb_read; 63static d_write_t fb_write; 64static d_ioctl_t fb_ioctl; 65static d_mmap_t fb_mmap; 66 67static struct cdevsw fb_cdevsw = { 68 .d_version = D_VERSION, 69 .d_flags = D_NEEDGIANT, 70 .d_open = fb_open, 71 .d_close = fb_close, 72 .d_read = fb_read, 73 .d_write = fb_write, 74 .d_ioctl = fb_ioctl, 75 .d_mmap = fb_mmap, 76 .d_name = "fb", 77}; 78 79static int framebuffer_dev_unit = 0; 80 81static int 82fb_open(struct cdev *dev, int oflags, int devtype, struct thread *td) 83{ 84 85 return (0); 86} 87 88static int 89fb_close(struct cdev *dev, int fflag, int devtype, struct thread *td) 90{ 91 92 return (0); 93} 94 95static int 96fb_ioctl(struct cdev *dev, u_long cmd, caddr_t data, int fflag, 97 struct thread *td) 98{ 99 100 return (0); 101} 102 103static int 104fb_read(struct cdev *dev, struct uio *uio, int ioflag) 105{ 106 107 return (0); /* XXX nothing to read, yet */ 108} 109 110static int 111fb_write(struct cdev *dev, struct uio *uio, int ioflag) 112{ 113 114 return (0); /* XXX nothing written */ 115} 116 117static int 118fb_mmap(struct cdev *dev, vm_ooffset_t offset, vm_paddr_t *paddr, int nprot, 119 vm_memattr_t *memattr) 120{ 121 struct fb_info *info; 122 123 info = dev->si_drv1; 124 if (offset < info->fb_size) { 125 *paddr = info->fb_pbase + offset; 126 return (0); 127 } 128 return (EINVAL); 129} 130 131 132static void 133vt_fb_mem_wr1(struct fb_info *sc, uint32_t o, uint8_t v) 134{ 135 136 KASSERT((o < sc->fb_size), ("Offset %#08x out of fb size", o)); 137 *(uint8_t *)(sc->fb_vbase + o) = v; 138} 139 140static void 141vt_fb_mem_wr2(struct fb_info *sc, uint32_t o, uint16_t v) 142{ 143 144 KASSERT((o < sc->fb_size), ("Offset %#08x out of fb size", o)); 145 *(uint16_t *)(sc->fb_vbase + o) = v; 146} 147 148static void 149vt_fb_mem_wr4(struct fb_info *sc, uint32_t o, uint32_t v) 150{ 151 152 KASSERT((o < sc->fb_size), ("Offset %#08x out of fb size", o)); 153 *(uint32_t *)(sc->fb_vbase + o) = v; 154} 155 156static void 157vt_fb_indir_wr1(struct fb_info *sc, uint32_t o, uint8_t v) 158{ 159 160 KASSERT((o < sc->fb_size), ("Offset %#08x out of fb size", o)); 161 sc->fb_write(sc->fb_priv, o, &v, 1); 162} 163 164static void 165vt_fb_indir_wr2(struct fb_info *sc, uint32_t o, uint16_t v) 166{ 167 168 KASSERT((o < sc->fb_size), ("Offset %#08x out of fb size", o)); 169 sc->fb_write(sc->fb_priv, o, &v, 2); 170} 171 172static void 173vt_fb_indir_wr4(struct fb_info *sc, uint32_t o, uint32_t v) 174{ 175 176 KASSERT((o < sc->fb_size), ("Offset %#08x out of fb size", o)); 177 sc->fb_write(sc->fb_priv, o, &v, 4); 178} 179 180static int 181fb_probe(struct fb_info *info) 182{ 183 184 if (info->fb_size == 0) 185 return (ENXIO); 186 187 if (info->fb_write != NULL) { 188 if (info->fb_write == NULL) { 189 return (EINVAL); 190 } 191 info->fb_flags |= FB_FLAG_NOMMAP; 192 info->wr1 = &vt_fb_indir_wr1; 193 info->wr2 = &vt_fb_indir_wr2; 194 info->wr4 = &vt_fb_indir_wr4; 195 } else if (info->fb_vbase != 0) { 196 if (info->fb_pbase == 0) 197 info->fb_flags |= FB_FLAG_NOMMAP; 198 info->wr1 = &vt_fb_mem_wr1; 199 info->wr2 = &vt_fb_mem_wr2; 200 info->wr4 = &vt_fb_mem_wr4; 201 } else 202 return (ENXIO); 203 204 return (0); 205} 206 207 208static int 209fb_init(struct fb_list_entry *entry, int unit) 210{ 211 struct fb_info *info; 212 213 info = entry->fb_info; 214 entry->fb_si = make_dev(&fb_cdevsw, unit, UID_ROOT, GID_WHEEL, 215 0600, "fb%d", unit); 216 entry->fb_si->si_drv1 = info; 217 218 return (0); 219} 220 221int 222fbd_list() 223{ 224 struct fb_list_entry *entry; 225 226 if (LIST_EMPTY(&fb_list_head)) 227 return (ENOENT); 228 229 LIST_FOREACH(entry, &fb_list_head, fb_list) { 230 printf("FB %s @%p\n", entry->fb_info->fb_name, 231 (void *)entry->fb_info->fb_pbase); 232 } 233 234 return (0); 235} 236 237static struct fb_list_entry * 238fbd_find(struct fb_info* info) 239{ 240 struct fb_list_entry *entry, *tmp; 241 242 LIST_FOREACH_SAFE(entry, &fb_list_head, fb_list, tmp) { 243 if (entry->fb_info == info) { 244 return (entry); 245 } 246 } 247 248 return (NULL); 249} 250 251int 252fbd_register(struct fb_info* info) 253{ 254 struct fb_list_entry *entry; 255 int err, first; 256 257 first = 0; 258 if (LIST_EMPTY(&fb_list_head)) 259 first++; 260 261 entry = fbd_find(info); 262 if (entry != NULL) { 263 /* XXX Update framebuffer params */ 264 return (0); 265 } 266 267 err = fb_probe(info); 268 if (err) 269 return (err); 270 271 entry = malloc(sizeof(struct fb_list_entry), M_DEVBUF, M_WAITOK|M_ZERO); 272 entry->fb_info = info; 273 274 LIST_INSERT_HEAD(&fb_list_head, entry, fb_list); 275 276 err = fb_init(entry, framebuffer_dev_unit++); 277 if (err) 278 return (err); 279 280 if (first) 281 vt_fb_attach(info); 282 283 return (0); 284} 285 286int 287fbd_unregister(struct fb_info* info) 288{ 289 struct fb_list_entry *entry, *tmp; 290 291 LIST_FOREACH_SAFE(entry, &fb_list_head, fb_list, tmp) { 292 if (entry->fb_info == info) { 293 LIST_REMOVE(entry, fb_list); 294 free(entry, M_DEVBUF); 295 return (0); 296 } 297 } 298 299 return (ENOENT); 300} 301 302static void 303register_fb_wrap(void *arg, void *ptr) 304{ 305 306 fbd_register((struct fb_info *)ptr); 307} 308 309static void 310unregister_fb_wrap(void *arg, void *ptr) 311{ 312 313 fbd_unregister((struct fb_info *)ptr); 314} 315 316static void 317fbd_evh_init(void *ctx) 318{ 319 320 EVENTHANDLER_REGISTER(register_framebuffer, register_fb_wrap, NULL, 321 EVENTHANDLER_PRI_ANY); 322 EVENTHANDLER_REGISTER(unregister_framebuffer, unregister_fb_wrap, NULL, 323 EVENTHANDLER_PRI_ANY); 324} 325