| /* |
| * Copyright (C) 2008 The Android Open Source Project |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| |
| #include <stdio.h> |
| #include <stdlib.h> |
| #include <unistd.h> |
| #include <fcntl.h> |
| #include <sys/mman.h> |
| #include <sys/stat.h> |
| #include <sys/types.h> |
| |
| #include <linux/fb.h> |
| #include <linux/kd.h> |
| |
| #include "log.h" |
| |
| #ifdef ANDROID |
| #include <cutils/memory.h> |
| #else |
| void android_memset16(void *_ptr, unsigned short val, unsigned count) |
| { |
| unsigned short *ptr = _ptr; |
| count >>= 1; |
| while(count--) |
| *ptr++ = val; |
| } |
| #endif |
| |
| struct FB { |
| unsigned short *bits; |
| unsigned size; |
| int fd; |
| struct fb_fix_screeninfo fi; |
| struct fb_var_screeninfo vi; |
| }; |
| |
| #define fb_width(fb) ((fb)->vi.xres) |
| #define fb_height(fb) ((fb)->vi.yres) |
| #define fb_size(fb) ((fb)->vi.xres * (fb)->vi.yres * 2) |
| |
| static int fb_open(struct FB *fb) |
| { |
| fb->fd = open("/dev/graphics/fb0", O_RDWR); |
| if (fb->fd < 0) |
| return -1; |
| |
| if (ioctl(fb->fd, FBIOGET_FSCREENINFO, &fb->fi) < 0) |
| goto fail; |
| if (ioctl(fb->fd, FBIOGET_VSCREENINFO, &fb->vi) < 0) |
| goto fail; |
| |
| fb->bits = mmap(0, fb_size(fb), PROT_READ | PROT_WRITE, |
| MAP_SHARED, fb->fd, 0); |
| if (fb->bits == MAP_FAILED) |
| goto fail; |
| |
| return 0; |
| |
| fail: |
| close(fb->fd); |
| return -1; |
| } |
| |
| static void fb_close(struct FB *fb) |
| { |
| munmap(fb->bits, fb_size(fb)); |
| close(fb->fd); |
| } |
| |
| /* there's got to be a more portable way to do this ... */ |
| static void fb_update(struct FB *fb) |
| { |
| fb->vi.yoffset = 1; |
| ioctl(fb->fd, FBIOPUT_VSCREENINFO, &fb->vi); |
| fb->vi.yoffset = 0; |
| ioctl(fb->fd, FBIOPUT_VSCREENINFO, &fb->vi); |
| } |
| |
| static int vt_set_mode(int graphics) |
| { |
| int fd, r; |
| fd = open("/dev/tty0", O_RDWR | O_SYNC); |
| if (fd < 0) |
| return -1; |
| r = ioctl(fd, KDSETMODE, (void*) (graphics ? KD_GRAPHICS : KD_TEXT)); |
| close(fd); |
| return r; |
| } |
| |
| /* 565RLE image format: [count(2 bytes), rle(2 bytes)] */ |
| |
| int load_565rle_image(char *fn) |
| { |
| struct FB fb; |
| struct stat s; |
| unsigned short *data, *bits, *ptr; |
| unsigned count, max; |
| int fd; |
| |
| if (vt_set_mode(1)) |
| return -1; |
| |
| fd = open(fn, O_RDONLY); |
| if (fd < 0) { |
| ERROR("cannot open '%s'\n", fn); |
| goto fail_restore_text; |
| } |
| |
| if (fstat(fd, &s) < 0) { |
| goto fail_close_file; |
| } |
| |
| data = mmap(0, s.st_size, PROT_READ, MAP_SHARED, fd, 0); |
| if (data == MAP_FAILED) |
| goto fail_close_file; |
| |
| if (fb_open(&fb)) |
| goto fail_unmap_data; |
| |
| max = fb_width(&fb) * fb_height(&fb); |
| ptr = data; |
| count = s.st_size; |
| bits = fb.bits; |
| while (count > 3) { |
| unsigned n = ptr[0]; |
| if (n > max) |
| break; |
| android_memset16(bits, ptr[1], n << 1); |
| bits += n; |
| max -= n; |
| ptr += 2; |
| count -= 4; |
| } |
| |
| munmap(data, s.st_size); |
| fb_update(&fb); |
| fb_close(&fb); |
| close(fd); |
| unlink(fn); |
| return 0; |
| |
| fail_unmap_data: |
| munmap(data, s.st_size); |
| fail_close_file: |
| close(fd); |
| fail_restore_text: |
| vt_set_mode(0); |
| return -1; |
| } |
| |