c798c9cd24
In order to maintain compatibility with older trees, we now have minadbd.old and minui.old. I had to use a TARGET_GLOBAL_CFLAG to handle ifdef issues in minui/minui.d because healthd includes minui/minui.h and there was no other alternative to make minui.h compatible with older trees without having to modify healthd rules which is outside of TWRP. Note that the new minui does not currently have support for qcom overlay graphics. Support for this graphics mode will likely be added in a later patch set. If you are building in a 6.0 tree and have a device that needs qcom overlay graphics, be warned, as off mode charging may not work properly. A dead battery in this case could potentially brick your device if it is unable to charge as healthd handles charging duties. Update rules for building toolbox and add rules for making toybox Use permissive.sh in init.rc which will follow symlinks so we do not have to worry about what binary is supplying the setenforce functionality (toolbox, toybox, or busybox). Fix a few warnings in the main recovery binary source code. Fix a few includes that were missing that prevented compiling in 6.0 Change-Id: Ia67aa2107d260883da5e365475a19bea538e8b97
511 lines
13 KiB
C
511 lines
13 KiB
C
/*
|
|
* Copyright (C) 2007 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 <stdbool.h>
|
|
#include <stdlib.h>
|
|
#include <unistd.h>
|
|
|
|
#include <errno.h>
|
|
#include <fcntl.h>
|
|
#include <stdio.h>
|
|
|
|
#include <sys/ioctl.h>
|
|
#include <sys/mman.h>
|
|
#include <sys/types.h>
|
|
|
|
#include <linux/fb.h>
|
|
#include <linux/kd.h>
|
|
|
|
#include <pixelflinger/pixelflinger.h>
|
|
|
|
#ifdef BOARD_USE_CUSTOM_RECOVERY_FONT
|
|
#include BOARD_USE_CUSTOM_RECOVERY_FONT
|
|
#else
|
|
#include "font_10x18.h"
|
|
#endif
|
|
|
|
#include "minui.h"
|
|
|
|
#if defined(RECOVERY_BGRA)
|
|
#define PIXEL_FORMAT GGL_PIXEL_FORMAT_BGRA_8888
|
|
#define PIXEL_SIZE 4
|
|
#elif defined(RECOVERY_RGBX)
|
|
#define PIXEL_FORMAT GGL_PIXEL_FORMAT_RGBX_8888
|
|
#define PIXEL_SIZE 4
|
|
#else
|
|
#define PIXEL_FORMAT GGL_PIXEL_FORMAT_RGB_565
|
|
#define PIXEL_SIZE 2
|
|
#endif
|
|
|
|
#define NUM_BUFFERS 2
|
|
|
|
typedef struct {
|
|
GGLSurface* texture;
|
|
unsigned cwidth;
|
|
unsigned cheight;
|
|
} GRFont;
|
|
|
|
static GRFont *gr_font = 0;
|
|
static GGLContext *gr_context = 0;
|
|
static GGLSurface gr_font_texture;
|
|
static GGLSurface gr_framebuffer[NUM_BUFFERS];
|
|
static GGLSurface gr_mem_surface;
|
|
static unsigned gr_active_fb = 0;
|
|
static unsigned double_buffering = 0;
|
|
static int overscan_percent = OVERSCAN_PERCENT;
|
|
static int overscan_offset_x = 0;
|
|
static int overscan_offset_y = 0;
|
|
|
|
static int gr_fb_fd = -1;
|
|
static int gr_vt_fd = -1;
|
|
|
|
static struct fb_var_screeninfo vi;
|
|
static struct fb_fix_screeninfo fi;
|
|
|
|
static bool has_overlay = false;
|
|
|
|
bool target_has_overlay(char *version);
|
|
int free_ion_mem(void);
|
|
int alloc_ion_mem(unsigned int size);
|
|
int allocate_overlay(int fd, GGLSurface gr_fb[]);
|
|
int free_overlay(int fd);
|
|
int overlay_display_frame(int fd, GGLubyte* data, size_t size);
|
|
|
|
static int get_framebuffer(GGLSurface *fb)
|
|
{
|
|
int fd;
|
|
void *bits;
|
|
|
|
fd = open("/dev/graphics/fb0", O_RDWR);
|
|
if (fd < 0) {
|
|
perror("cannot open fb0");
|
|
return -1;
|
|
}
|
|
|
|
if (ioctl(fd, FBIOGET_VSCREENINFO, &vi) < 0) {
|
|
perror("failed to get fb0 info");
|
|
close(fd);
|
|
return -1;
|
|
}
|
|
|
|
vi.bits_per_pixel = PIXEL_SIZE * 8;
|
|
if (PIXEL_FORMAT == GGL_PIXEL_FORMAT_BGRA_8888) {
|
|
vi.red.offset = 8;
|
|
vi.red.length = 8;
|
|
vi.green.offset = 16;
|
|
vi.green.length = 8;
|
|
vi.blue.offset = 24;
|
|
vi.blue.length = 8;
|
|
vi.transp.offset = 0;
|
|
vi.transp.length = 8;
|
|
} else if (PIXEL_FORMAT == GGL_PIXEL_FORMAT_RGBX_8888) {
|
|
vi.red.offset = 24;
|
|
vi.red.length = 8;
|
|
vi.green.offset = 16;
|
|
vi.green.length = 8;
|
|
vi.blue.offset = 8;
|
|
vi.blue.length = 8;
|
|
vi.transp.offset = 0;
|
|
vi.transp.length = 8;
|
|
} else { /* RGB565*/
|
|
vi.red.offset = 11;
|
|
vi.red.length = 5;
|
|
vi.green.offset = 5;
|
|
vi.green.length = 6;
|
|
vi.blue.offset = 0;
|
|
vi.blue.length = 5;
|
|
vi.transp.offset = 0;
|
|
vi.transp.length = 0;
|
|
}
|
|
if (ioctl(fd, FBIOPUT_VSCREENINFO, &vi) < 0) {
|
|
perror("failed to put fb0 info");
|
|
close(fd);
|
|
return -1;
|
|
}
|
|
|
|
if (ioctl(fd, FBIOGET_FSCREENINFO, &fi) < 0) {
|
|
perror("failed to get fb0 info");
|
|
close(fd);
|
|
return -1;
|
|
}
|
|
|
|
has_overlay = target_has_overlay(fi.id);
|
|
|
|
if (!has_overlay) {
|
|
bits = mmap(0, fi.smem_len, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
|
|
if (bits == MAP_FAILED) {
|
|
perror("failed to mmap framebuffer");
|
|
close(fd);
|
|
return -1;
|
|
}
|
|
}
|
|
|
|
overscan_offset_x = vi.xres * overscan_percent / 100;
|
|
overscan_offset_y = vi.yres * overscan_percent / 100;
|
|
|
|
fb->version = sizeof(*fb);
|
|
fb->width = vi.xres;
|
|
fb->height = vi.yres;
|
|
fb->stride = fi.line_length/PIXEL_SIZE;
|
|
fb->format = PIXEL_FORMAT;
|
|
if (!has_overlay) {
|
|
fb->data = bits;
|
|
memset(fb->data, 0, vi.yres * fi.line_length);
|
|
}
|
|
|
|
fb++;
|
|
|
|
/* check if we can use double buffering */
|
|
if (vi.yres * fi.line_length * 2 > fi.smem_len)
|
|
return fd;
|
|
|
|
double_buffering = 1;
|
|
|
|
fb->version = sizeof(*fb);
|
|
fb->width = vi.xres;
|
|
fb->height = vi.yres;
|
|
fb->stride = fi.line_length/PIXEL_SIZE;
|
|
fb->format = PIXEL_FORMAT;
|
|
if (!has_overlay) {
|
|
fb->data = (void*) (((unsigned long) bits) + vi.yres * fi.line_length);
|
|
memset(fb->data, 0, vi.yres * fi.line_length);
|
|
}
|
|
|
|
return fd;
|
|
}
|
|
|
|
static void get_memory_surface(GGLSurface* ms) {
|
|
ms->version = sizeof(*ms);
|
|
ms->width = vi.xres;
|
|
ms->height = vi.yres;
|
|
ms->stride = fi.line_length/PIXEL_SIZE;
|
|
ms->data = malloc(fi.line_length * vi.yres);
|
|
ms->format = PIXEL_FORMAT;
|
|
}
|
|
|
|
static void set_active_framebuffer(unsigned n)
|
|
{
|
|
if (n > 1 || !double_buffering) return;
|
|
vi.yres_virtual = vi.yres * NUM_BUFFERS;
|
|
vi.yoffset = n * vi.yres;
|
|
vi.bits_per_pixel = PIXEL_SIZE * 8;
|
|
if (ioctl(gr_fb_fd, FBIOPUT_VSCREENINFO, &vi) < 0) {
|
|
perror("active fb swap failed");
|
|
}
|
|
}
|
|
|
|
void gr_flip(void)
|
|
{
|
|
if (-EINVAL == overlay_display_frame(gr_fb_fd, gr_mem_surface.data,
|
|
(fi.line_length * vi.yres))) {
|
|
GGLContext *gl = gr_context;
|
|
|
|
/* swap front and back buffers */
|
|
if (double_buffering)
|
|
gr_active_fb = (gr_active_fb + 1) & 1;
|
|
|
|
/* copy data from the in-memory surface to the buffer we're about
|
|
* to make active. */
|
|
memcpy(gr_framebuffer[gr_active_fb].data, gr_mem_surface.data,
|
|
fi.line_length * vi.yres);
|
|
|
|
/* inform the display driver */
|
|
set_active_framebuffer(gr_active_fb);
|
|
}
|
|
}
|
|
|
|
void gr_color(unsigned char r, unsigned char g, unsigned char b, unsigned char a)
|
|
{
|
|
GGLContext *gl = gr_context;
|
|
GGLint color[4];
|
|
color[0] = ((r << 8) | r) + 1;
|
|
color[1] = ((g << 8) | g) + 1;
|
|
color[2] = ((b << 8) | b) + 1;
|
|
color[3] = ((a << 8) | a) + 1;
|
|
gl->color4xv(gl, color);
|
|
}
|
|
|
|
int gr_measure(const char *s)
|
|
{
|
|
return gr_font->cwidth * strlen(s);
|
|
}
|
|
|
|
void gr_font_size(int *x, int *y)
|
|
{
|
|
*x = gr_font->cwidth;
|
|
*y = gr_font->cheight;
|
|
}
|
|
|
|
int gr_text(int x, int y, const char *s, ...)
|
|
{
|
|
return gr_text_impl(x, y, s, 0);
|
|
}
|
|
|
|
int gr_text_impl(int x, int y, const char *s, int bold)
|
|
{
|
|
GGLContext *gl = gr_context;
|
|
GRFont *font = gr_font;
|
|
unsigned off;
|
|
|
|
if (!font->texture) return x;
|
|
|
|
bold = bold && (font->texture->height != font->cheight);
|
|
|
|
x += overscan_offset_x;
|
|
y += overscan_offset_y;
|
|
|
|
gl->bindTexture(gl, font->texture);
|
|
gl->texEnvi(gl, GGL_TEXTURE_ENV, GGL_TEXTURE_ENV_MODE, GGL_REPLACE);
|
|
gl->texGeni(gl, GGL_S, GGL_TEXTURE_GEN_MODE, GGL_ONE_TO_ONE);
|
|
gl->texGeni(gl, GGL_T, GGL_TEXTURE_GEN_MODE, GGL_ONE_TO_ONE);
|
|
gl->enable(gl, GGL_TEXTURE_2D);
|
|
|
|
while((off = *s++)) {
|
|
off -= 32;
|
|
if (off < 96) {
|
|
gl->texCoord2i(gl, (off * font->cwidth) - x,
|
|
(bold ? font->cheight : 0) - y);
|
|
gl->recti(gl, x, y, x + font->cwidth, y + font->cheight);
|
|
}
|
|
x += font->cwidth;
|
|
}
|
|
|
|
return x;
|
|
}
|
|
|
|
void gr_texticon(int x, int y, gr_surface icon) {
|
|
if (gr_context == NULL || icon == NULL) {
|
|
return;
|
|
}
|
|
GGLContext* gl = gr_context;
|
|
|
|
x += overscan_offset_x;
|
|
y += overscan_offset_y;
|
|
|
|
gl->bindTexture(gl, (GGLSurface*) icon);
|
|
gl->texEnvi(gl, GGL_TEXTURE_ENV, GGL_TEXTURE_ENV_MODE, GGL_REPLACE);
|
|
gl->texGeni(gl, GGL_S, GGL_TEXTURE_GEN_MODE, GGL_ONE_TO_ONE);
|
|
gl->texGeni(gl, GGL_T, GGL_TEXTURE_GEN_MODE, GGL_ONE_TO_ONE);
|
|
gl->enable(gl, GGL_TEXTURE_2D);
|
|
|
|
int w = gr_get_width(icon);
|
|
int h = gr_get_height(icon);
|
|
|
|
gl->texCoord2i(gl, -x, -y);
|
|
gl->recti(gl, x, y, x+gr_get_width(icon), y+gr_get_height(icon));
|
|
}
|
|
|
|
void gr_fill(int x1, int y1, int x2, int y2)
|
|
{
|
|
x1 += overscan_offset_x;
|
|
y1 += overscan_offset_y;
|
|
|
|
x2 += overscan_offset_x;
|
|
y2 += overscan_offset_y;
|
|
|
|
GGLContext *gl = gr_context;
|
|
gl->disable(gl, GGL_TEXTURE_2D);
|
|
gl->recti(gl, x1, y1, x2, y2);
|
|
}
|
|
|
|
void gr_blit(gr_surface source, int sx, int sy, int w, int h, int dx, int dy) {
|
|
if (gr_context == NULL || source == NULL) {
|
|
return;
|
|
}
|
|
GGLContext *gl = gr_context;
|
|
|
|
dx += overscan_offset_x;
|
|
dy += overscan_offset_y;
|
|
|
|
gl->bindTexture(gl, (GGLSurface*) source);
|
|
gl->texEnvi(gl, GGL_TEXTURE_ENV, GGL_TEXTURE_ENV_MODE, GGL_REPLACE);
|
|
gl->texGeni(gl, GGL_S, GGL_TEXTURE_GEN_MODE, GGL_ONE_TO_ONE);
|
|
gl->texGeni(gl, GGL_T, GGL_TEXTURE_GEN_MODE, GGL_ONE_TO_ONE);
|
|
gl->enable(gl, GGL_TEXTURE_2D);
|
|
gl->texCoord2i(gl, sx - dx, sy - dy);
|
|
gl->recti(gl, dx, dy, dx + w, dy + h);
|
|
}
|
|
|
|
unsigned int gr_get_width(gr_surface surface) {
|
|
if (surface == NULL) {
|
|
return 0;
|
|
}
|
|
return ((GGLSurface*) surface)->width;
|
|
}
|
|
|
|
unsigned int gr_get_height(gr_surface surface) {
|
|
if (surface == NULL) {
|
|
return 0;
|
|
}
|
|
return ((GGLSurface*) surface)->height;
|
|
}
|
|
|
|
static void gr_init_font(void)
|
|
{
|
|
gr_font = calloc(sizeof(*gr_font), 1);
|
|
|
|
int res = res_create_surface("font", (void**)&(gr_font->texture));
|
|
if (res == 0) {
|
|
// The font image should be a 96x2 array of character images. The
|
|
// columns are the printable ASCII characters 0x20 - 0x7f. The
|
|
// top row is regular text; the bottom row is bold.
|
|
gr_font->cwidth = gr_font->texture->width / 96;
|
|
gr_font->cheight = gr_font->texture->height / 2;
|
|
} else {
|
|
printf("failed to read font: res=%d\n", res);
|
|
|
|
// fall back to the compiled-in font.
|
|
gr_font->texture = malloc(sizeof(*gr_font->texture));
|
|
gr_font->texture->width = font.width;
|
|
gr_font->texture->height = font.height;
|
|
gr_font->texture->stride = font.width;
|
|
|
|
unsigned char* bits = malloc(font.width * font.height);
|
|
gr_font->texture->data = (void*) bits;
|
|
|
|
unsigned char data;
|
|
unsigned char* in = font.rundata;
|
|
while((data = *in++)) {
|
|
memset(bits, (data & 0x80) ? 255 : 0, data & 0x7f);
|
|
bits += (data & 0x7f);
|
|
}
|
|
|
|
gr_font->cwidth = font.cwidth;
|
|
gr_font->cheight = font.cheight;
|
|
}
|
|
|
|
// interpret the grayscale as alpha
|
|
gr_font->texture->format = GGL_PIXEL_FORMAT_A_8;
|
|
}
|
|
|
|
int gr_init(void)
|
|
{
|
|
gglInit(&gr_context);
|
|
GGLContext *gl = gr_context;
|
|
|
|
gr_init_font();
|
|
gr_vt_fd = open("/dev/tty0", O_RDWR | O_SYNC);
|
|
if (gr_vt_fd < 0) {
|
|
// This is non-fatal; post-Cupcake kernels don't have tty0.
|
|
perror("can't open /dev/tty0");
|
|
} else if (ioctl(gr_vt_fd, KDSETMODE, (void*) KD_GRAPHICS)) {
|
|
// However, if we do open tty0, we expect the ioctl to work.
|
|
perror("failed KDSETMODE to KD_GRAPHICS on tty0");
|
|
gr_exit();
|
|
return -1;
|
|
}
|
|
|
|
gr_fb_fd = get_framebuffer(gr_framebuffer);
|
|
if (gr_fb_fd < 0) {
|
|
gr_exit();
|
|
return -1;
|
|
}
|
|
|
|
get_memory_surface(&gr_mem_surface);
|
|
|
|
fprintf(stderr, "framebuffer: fd %d (%d x %d)\n",
|
|
gr_fb_fd, gr_framebuffer[0].width, gr_framebuffer[0].height);
|
|
|
|
/* start with 0 as front (displayed) and 1 as back (drawing) */
|
|
gr_active_fb = 0;
|
|
if (!has_overlay)
|
|
set_active_framebuffer(0);
|
|
gl->colorBuffer(gl, &gr_mem_surface);
|
|
|
|
gl->activeTexture(gl, 0);
|
|
gl->enable(gl, GGL_BLEND);
|
|
gl->blendFunc(gl, GGL_SRC_ALPHA, GGL_ONE_MINUS_SRC_ALPHA);
|
|
|
|
gr_fb_blank(true);
|
|
gr_fb_blank(false);
|
|
|
|
if (!alloc_ion_mem(fi.line_length * vi.yres))
|
|
allocate_overlay(gr_fb_fd, gr_framebuffer);
|
|
|
|
return 0;
|
|
}
|
|
|
|
void gr_exit(void)
|
|
{
|
|
free_overlay(gr_fb_fd);
|
|
free_ion_mem();
|
|
|
|
close(gr_fb_fd);
|
|
gr_fb_fd = -1;
|
|
|
|
free(gr_mem_surface.data);
|
|
|
|
ioctl(gr_vt_fd, KDSETMODE, (void*) KD_TEXT);
|
|
close(gr_vt_fd);
|
|
gr_vt_fd = -1;
|
|
}
|
|
|
|
int gr_fb_width(void)
|
|
{
|
|
return gr_framebuffer[0].width - 2*overscan_offset_x;
|
|
}
|
|
|
|
int gr_fb_height(void)
|
|
{
|
|
return gr_framebuffer[0].height - 2*overscan_offset_y;
|
|
}
|
|
|
|
gr_pixel *gr_fb_data(void)
|
|
{
|
|
return (unsigned short *) gr_mem_surface.data;
|
|
}
|
|
|
|
void gr_fb_blank(bool blank)
|
|
{
|
|
#if defined(TW_NO_SCREEN_BLANK) && defined(TW_BRIGHTNESS_PATH) && defined(TW_MAX_BRIGHTNESS)
|
|
int fd;
|
|
char brightness[4];
|
|
snprintf(brightness, 4, "%03d", TW_MAX_BRIGHTNESS/2);
|
|
|
|
fd = open(TW_BRIGHTNESS_PATH, O_RDWR);
|
|
if (fd < 0) {
|
|
perror("cannot open LCD backlight");
|
|
return;
|
|
}
|
|
write(fd, blank ? "000" : brightness, 3);
|
|
close(fd);
|
|
#else
|
|
int ret;
|
|
if (blank)
|
|
free_overlay(gr_fb_fd);
|
|
|
|
ret = ioctl(gr_fb_fd, FBIOBLANK, blank ? FB_BLANK_POWERDOWN : FB_BLANK_UNBLANK);
|
|
if (ret < 0)
|
|
perror("ioctl(): blank");
|
|
|
|
if (!blank)
|
|
allocate_overlay(gr_fb_fd, gr_framebuffer);
|
|
#endif
|
|
}
|
|
|
|
void gr_get_memory_surface(gr_surface surface)
|
|
{
|
|
get_memory_surface( (GGLSurface*) surface);
|
|
}
|
|
|
|
// These are new graphics functions from 5.0 that were not available in
|
|
// 4.4 that are required by charger and healthd
|
|
void gr_clear()
|
|
{
|
|
return;
|
|
}
|
|
|