| /* | |
| * 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; | |
| } | |