Home | History | Annotate | Download | only in recovery
      1 /*
      2  * Copyright (C) 2011 The Android Open Source Project
      3  *
      4  * Licensed under the Apache License, Version 2.0 (the "License");
      5  * you may not use this file except in compliance with the License.
      6  * You may obtain a copy of the License at
      7  *
      8  *      http://www.apache.org/licenses/LICENSE-2.0
      9  *
     10  * Unless required by applicable law or agreed to in writing, software
     11  * distributed under the License is distributed on an "AS IS" BASIS,
     12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     13  * See the License for the specific language governing permissions and
     14  * limitations under the License.
     15  */
     16 
     17 #include <fcntl.h>
     18 #include <linux/fb.h>
     19 #include <linux/input.h>
     20 #include <pthread.h>
     21 #include <stdarg.h>
     22 #include <stdio.h>
     23 #include <stdlib.h>
     24 #include <sys/ioctl.h>
     25 #include <sys/time.h>
     26 #include <sys/types.h>
     27 #include <time.h>
     28 #include <unistd.h>
     29 #include <linux/ioctl.h>
     30 
     31 #include "common.h"
     32 #include "device.h"
     33 #include "ui.h"
     34 #include "screen_ui.h"
     35 
     36 /* CEA-861 specifies a maximum of 65 modes in an EDID */
     37 #define CEA_MODEDB_SIZE 65
     38 static const char* HEADERS[] = { "Use hardware button to move cursor; long-press to select item.",
     39                                  "",
     40                                  NULL };
     41 
     42 // these strings are never actually displayed
     43 static const char* ITEMS[] =  {"reboot system now",
     44                                "apply update from ADB",
     45                                "wipe data/factory reset",
     46                                "wipe cache partition",
     47                                "view recovery logs",
     48                                NULL };
     49 
     50 #define kFBDevice "/dev/graphics/fb0"
     51 #define FBIO_PSB_SET_RGBX       _IOWR('F', 0x42, struct fb_var_screeninfo)
     52 #define FBIO_PSB_SET_RMODE      _IOWR('F', 0x43, struct fb_var_screeninfo)
     53 
     54 struct led_rgb_vals {
     55         uint8_t rgb[3];
     56 };
     57 
     58 // Return the current time as a double (including fractions of a second).
     59 static double now() {
     60     struct timeval tv;
     61     gettimeofday(&tv, NULL);
     62     return tv.tv_sec + tv.tv_usec / 1000000.0;
     63 }
     64 
     65 class FuguUI : public ScreenRecoveryUI {
     66 public:
     67     FuguUI() :
     68         text_visible(false),
     69         text_ever_visible(false),
     70         background_mode(NONE),
     71         up_keys(0),
     72         next_key_pos(0),
     73         pending_select(false),
     74         long_press(false) {
     75         pthread_mutex_init(&long_mu, NULL);
     76         memset(last_keys, 0, kKeyBufferSize * sizeof(int));
     77     }
     78 
     79     void Init() {
     80         SetupDisplayMode();
     81         ScreenRecoveryUI::Init();
     82     }
     83 
     84     void SetupDisplayMode() {
     85         int fb_dev = open(kFBDevice, O_RDWR);
     86         int res;
     87         uint32_t i;
     88         printf("opening fb %s\n", kFBDevice);
     89         if (fb_dev < 0) {
     90             fprintf(stderr, "FAIL: failed to open \"%s\" (errno = %d)\n", kFBDevice, errno);
     91             return;
     92         }
     93 
     94         struct fb_var_screeninfo current_mode;
     95 
     96         res = ioctl(fb_dev, FBIO_PSB_SET_RMODE, &current_mode);
     97         if (res) {
     98             fprintf(stderr,
     99                 "FAIL: unable to set RGBX mode on display controller (errno = %d)\n",
    100                 errno);
    101             return;
    102         }
    103 
    104         res = ioctl(fb_dev, FBIOGET_VSCREENINFO, &current_mode);
    105         if (res) {
    106             fprintf(stderr, "FAIL: unable to get mode, err %d\n", res);
    107             return;
    108         }
    109 
    110         res = ioctl(fb_dev, FBIOBLANK, FB_BLANK_POWERDOWN);
    111         if (res) {
    112             fprintf(stderr, "FAIL: unable to blank display, err %d\n", res);
    113             return;
    114         }
    115 
    116         current_mode.bits_per_pixel = 32;
    117         current_mode.red.offset = 0;
    118         current_mode.red.length = 8;
    119         current_mode.green.offset = 8;
    120         current_mode.green.length = 8;
    121         current_mode.blue.offset = 16;
    122         current_mode.blue.length = 8;
    123 
    124         res = ioctl(fb_dev, FBIOPUT_VSCREENINFO, &current_mode);
    125         if (res) {
    126             fprintf(stderr, "FAIL: unable to set mode, err %d\n", res);
    127             return;
    128         }
    129 
    130         /* set our display controller for RGBX */
    131         res = ioctl(fb_dev, FBIO_PSB_SET_RGBX, &current_mode);
    132         if (res) {
    133             fprintf(stderr,
    134                 "FAIL: unable to set RGBX mode on display controller (errno = %d)\n",
    135                 errno);
    136             return;
    137         }
    138 
    139         res = ioctl(fb_dev, FBIOBLANK, FB_BLANK_UNBLANK);
    140         if (res) {
    141             fprintf(stderr, "FAIL: unable to unblank display, err %d\n", res);
    142             return;
    143         }
    144     }
    145 
    146     void SetBackground(Icon icon) {
    147         ScreenRecoveryUI::SetBackground(icon);
    148 
    149         background_mode = icon;
    150     }
    151 
    152     void SetColor(UIElement e) {
    153         switch (e) {
    154             case HEADER:
    155                 gr_color(247, 0, 6, 255);
    156                 break;
    157             case MENU:
    158                 gr_color(0, 106, 157, 255);
    159                 break;
    160             case MENU_SEL_BG:
    161                 pthread_mutex_lock(&long_mu);
    162                 if (pending_select) {
    163                     gr_color(0, 156, 100, 255);
    164                 } else {
    165                     gr_color(0, 106, 157, 255);
    166                 }
    167                 pthread_mutex_unlock(&long_mu);
    168                 break;
    169             case MENU_SEL_FG:
    170                 gr_color(255, 255, 255, 255);
    171                 break;
    172             case LOG:
    173                 gr_color(249, 194, 0, 255);
    174                 break;
    175             case TEXT_FILL:
    176                 gr_color(0, 0, 0, 160);
    177                 break;
    178             default:
    179                 gr_color(255, 255, 255, 255);
    180                 break;
    181         }
    182     }
    183 
    184     void ShowText(bool visible) {
    185         ScreenRecoveryUI::ShowText(visible);
    186 
    187         text_ever_visible = text_ever_visible || visible;
    188         text_visible = visible;
    189     }
    190 
    191     bool IsTextVisible() {
    192         return text_visible;
    193     }
    194 
    195     bool WasTextEverVisible() {
    196         return text_ever_visible;
    197     }
    198 
    199     void Print(const char* fmt, ...) {
    200         char buf[256];
    201         va_list ap;
    202         va_start(ap, fmt);
    203         vsnprintf(buf, 256, fmt, ap);
    204         va_end(ap);
    205         ScreenRecoveryUI::Print("%s", buf);
    206     }
    207 
    208     void StartMenu(const char* const * headers, const char* const * items,
    209                    int initial_selection) {
    210         ScreenRecoveryUI::StartMenu(headers, items, initial_selection);
    211 
    212         menu_items = 0;
    213         for (const char* const * p = items; *p; ++p) {
    214             ++menu_items;
    215         }
    216     }
    217 
    218     int SelectMenu(int sel) {
    219         if (sel < 0) {
    220             sel += menu_items;
    221         }
    222         sel %= menu_items;
    223         ScreenRecoveryUI::SelectMenu(sel);
    224         return sel;
    225     }
    226 
    227     void NextCheckKeyIsLong(bool is_long_press) {
    228         long_press = is_long_press;
    229     }
    230 
    231     void KeyLongPress(int key) {
    232         pthread_mutex_lock(&long_mu);
    233         pending_select = true;
    234         pthread_mutex_unlock(&long_mu);
    235 
    236         Redraw();
    237     }
    238 
    239     KeyAction CheckKey(int key) {
    240         pthread_mutex_lock(&long_mu);
    241         pending_select = false;
    242         pthread_mutex_unlock(&long_mu);
    243 
    244         if (key == KEY_F1) {
    245             return MOUNT_SYSTEM;
    246         }
    247 
    248         if (long_press) {
    249             if (text_visible) {
    250                 EnqueueKey(KEY_ENTER);
    251                 return IGNORE;
    252             } else {
    253                 return TOGGLE;
    254             }
    255         } else {
    256             return text_visible ? ENQUEUE : IGNORE;
    257         }
    258     }
    259 
    260 private:
    261     static const int kKeyBufferSize = 100;
    262 
    263     int text_visible;
    264     int text_ever_visible;
    265 
    266     Icon background_mode;
    267 
    268     int up_keys;
    269     int next_key_pos;
    270     int last_keys[kKeyBufferSize];
    271 
    272     int menu_items;
    273 
    274     pthread_mutex_t long_mu;
    275     bool pending_select;
    276 
    277     bool long_press;
    278 };
    279 
    280 class FuguDevice : public Device {
    281   public:
    282     FuguDevice() :
    283         ui(new FuguUI) {
    284     }
    285 
    286     RecoveryUI* GetUI() { return ui; }
    287 
    288     int HandleMenuKey(int key, int visible) {
    289         static int running = 0;
    290 
    291         if (visible) {
    292             switch (key) {
    293                 case KEY_ENTER:
    294                     return kInvokeItem;
    295                     break;
    296 
    297                 case KEY_UP:
    298                     return kHighlightUp;
    299                     break;
    300 
    301                 case KEY_DOWN:
    302                 case KEY_CONNECT:   // the Fugu hardware button
    303                     return kHighlightDown;
    304                     break;
    305             }
    306         }
    307 
    308         return kNoAction;
    309     }
    310 
    311     BuiltinAction InvokeMenuItem(int menu_position) {
    312         switch (menu_position) {
    313           case 0: return REBOOT;
    314           case 1: return APPLY_ADB_SIDELOAD;
    315           case 2: return WIPE_DATA;
    316           case 3: return WIPE_CACHE;
    317           case 4: return READ_RECOVERY_LASTLOG;
    318           default: return NO_ACTION;
    319         }
    320     }
    321 
    322     const char* const* GetMenuHeaders() { return HEADERS; }
    323     const char* const* GetMenuItems() { return ITEMS; }
    324 
    325   private:
    326     RecoveryUI* ui;
    327 };
    328 
    329 Device* make_device() {
    330     return new FuguDevice();
    331 }
    332