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                                NULL };
     48 
     49 #define kFBDevice "/dev/graphics/fb0"
     50 #define FBIO_PSB_SET_RGBX       _IOWR('F', 0x42, struct fb_var_screeninfo)
     51 #define FBIO_PSB_SET_RMODE      _IOWR('F', 0x43, struct fb_var_screeninfo)
     52 
     53 struct led_rgb_vals {
     54         uint8_t rgb[3];
     55 };
     56 
     57 // Return the current time as a double (including fractions of a second).
     58 static double now() {
     59     struct timeval tv;
     60     gettimeofday(&tv, NULL);
     61     return tv.tv_sec + tv.tv_usec / 1000000.0;
     62 }
     63 
     64 class FuguUI : public ScreenRecoveryUI {
     65 public:
     66     FuguUI() :
     67         text_visible(false),
     68         text_ever_visible(false),
     69         background_mode(NONE),
     70         up_keys(0),
     71         next_key_pos(0),
     72         pending_select(false),
     73         long_press(false) {
     74         pthread_mutex_init(&long_mu, NULL);
     75         memset(last_keys, 0, kKeyBufferSize * sizeof(int));
     76     }
     77 
     78     void Init() {
     79         SetupDisplayMode();
     80         ScreenRecoveryUI::Init();
     81     }
     82 
     83     void SetupDisplayMode() {
     84         int fb_dev = open(kFBDevice, O_RDWR);
     85         int res;
     86         uint32_t i;
     87         printf("opening fb %s\n", kFBDevice);
     88         if (fb_dev < 0) {
     89             fprintf(stderr, "FAIL: failed to open \"%s\" (errno = %d)\n", kFBDevice, errno);
     90             return;
     91         }
     92 
     93         struct fb_var_screeninfo current_mode;
     94 
     95         res = ioctl(fb_dev, FBIO_PSB_SET_RMODE, &current_mode);
     96         if (res) {
     97             fprintf(stderr,
     98                 "FAIL: unable to set RGBX mode on display controller (errno = %d)\n",
     99                 errno);
    100             return;
    101         }
    102 
    103         res = ioctl(fb_dev, FBIOGET_VSCREENINFO, &current_mode);
    104         if (res) {
    105             fprintf(stderr, "FAIL: unable to get mode, err %d\n", res);
    106             return;
    107         }
    108 
    109         res = ioctl(fb_dev, FBIOBLANK, FB_BLANK_POWERDOWN);
    110         if (res) {
    111             fprintf(stderr, "FAIL: unable to blank display, err %d\n", res);
    112             return;
    113         }
    114 
    115         current_mode.bits_per_pixel = 32;
    116         current_mode.red.offset = 0;
    117         current_mode.red.length = 8;
    118         current_mode.green.offset = 8;
    119         current_mode.green.length = 8;
    120         current_mode.blue.offset = 16;
    121         current_mode.blue.length = 8;
    122 
    123         res = ioctl(fb_dev, FBIOPUT_VSCREENINFO, &current_mode);
    124         if (res) {
    125             fprintf(stderr, "FAIL: unable to set mode, err %d\n", res);
    126             return;
    127         }
    128 
    129         /* set our display controller for RGBX */
    130         res = ioctl(fb_dev, FBIO_PSB_SET_RGBX, &current_mode);
    131         if (res) {
    132             fprintf(stderr,
    133                 "FAIL: unable to set RGBX mode on display controller (errno = %d)\n",
    134                 errno);
    135             return;
    136         }
    137 
    138         res = ioctl(fb_dev, FBIOBLANK, FB_BLANK_UNBLANK);
    139         if (res) {
    140             fprintf(stderr, "FAIL: unable to unblank display, err %d\n", res);
    141             return;
    142         }
    143     }
    144 
    145     void SetBackground(Icon icon) {
    146         ScreenRecoveryUI::SetBackground(icon);
    147 
    148         background_mode = icon;
    149     }
    150 
    151     void SetColor(UIElement e) {
    152         switch (e) {
    153             case HEADER:
    154                 gr_color(247, 0, 6, 255);
    155                 break;
    156             case MENU:
    157                 gr_color(0, 106, 157, 255);
    158                 break;
    159             case MENU_SEL_BG:
    160                 pthread_mutex_lock(&long_mu);
    161                 if (pending_select) {
    162                     gr_color(0, 156, 100, 255);
    163                 } else {
    164                     gr_color(0, 106, 157, 255);
    165                 }
    166                 pthread_mutex_unlock(&long_mu);
    167                 break;
    168             case MENU_SEL_FG:
    169                 gr_color(255, 255, 255, 255);
    170                 break;
    171             case LOG:
    172                 gr_color(249, 194, 0, 255);
    173                 break;
    174             case TEXT_FILL:
    175                 gr_color(0, 0, 0, 160);
    176                 break;
    177             default:
    178                 gr_color(255, 255, 255, 255);
    179                 break;
    180         }
    181     }
    182 
    183     void ShowText(bool visible) {
    184         ScreenRecoveryUI::ShowText(visible);
    185 
    186         text_ever_visible = text_ever_visible || visible;
    187         text_visible = visible;
    188     }
    189 
    190     bool IsTextVisible() {
    191         return text_visible;
    192     }
    193 
    194     bool WasTextEverVisible() {
    195         return text_ever_visible;
    196     }
    197 
    198     void Print(const char* fmt, ...) {
    199         char buf[256];
    200         va_list ap;
    201         va_start(ap, fmt);
    202         vsnprintf(buf, 256, fmt, ap);
    203         va_end(ap);
    204         ScreenRecoveryUI::Print("%s", buf);
    205     }
    206 
    207     void StartMenu(const char* const * headers, const char* const * items,
    208                    int initial_selection) {
    209         ScreenRecoveryUI::StartMenu(headers, items, initial_selection);
    210 
    211         menu_items = 0;
    212         for (const char* const * p = items; *p; ++p) {
    213             ++menu_items;
    214         }
    215     }
    216 
    217     int SelectMenu(int sel) {
    218         if (sel < 0) {
    219             sel += menu_items;
    220         }
    221         sel %= menu_items;
    222         ScreenRecoveryUI::SelectMenu(sel);
    223         return sel;
    224     }
    225 
    226     void NextCheckKeyIsLong(bool is_long_press) {
    227         long_press = is_long_press;
    228     }
    229 
    230     void KeyLongPress(int key) {
    231         pthread_mutex_lock(&long_mu);
    232         pending_select = true;
    233         pthread_mutex_unlock(&long_mu);
    234 
    235         Redraw();
    236     }
    237 
    238     KeyAction CheckKey(int key) {
    239         pthread_mutex_lock(&long_mu);
    240         pending_select = false;
    241         pthread_mutex_unlock(&long_mu);
    242 
    243         if (key == KEY_F1) {
    244             return MOUNT_SYSTEM;
    245         }
    246 
    247         if (long_press) {
    248             if (text_visible) {
    249                 EnqueueKey(KEY_ENTER);
    250                 return IGNORE;
    251             } else {
    252                 return TOGGLE;
    253             }
    254         } else {
    255             return text_visible ? ENQUEUE : IGNORE;
    256         }
    257     }
    258 
    259 private:
    260     static const int kKeyBufferSize = 100;
    261 
    262     int text_visible;
    263     int text_ever_visible;
    264 
    265     Icon background_mode;
    266 
    267     int up_keys;
    268     int next_key_pos;
    269     int last_keys[kKeyBufferSize];
    270 
    271     int menu_items;
    272 
    273     pthread_mutex_t long_mu;
    274     bool pending_select;
    275 
    276     bool long_press;
    277 };
    278 
    279 class FuguDevice : public Device {
    280   public:
    281     FuguDevice() :
    282         ui(new FuguUI) {
    283     }
    284 
    285     RecoveryUI* GetUI() { return ui; }
    286 
    287     int HandleMenuKey(int key, int visible) {
    288         static int running = 0;
    289 
    290         if (visible) {
    291             switch (key) {
    292                 case KEY_ENTER:
    293                     return kInvokeItem;
    294                     break;
    295 
    296                 case KEY_UP:
    297                     return kHighlightUp;
    298                     break;
    299 
    300                 case KEY_DOWN:
    301                 case KEY_CONNECT:   // the Fugu hardware button
    302                     return kHighlightDown;
    303                     break;
    304             }
    305         }
    306 
    307         return kNoAction;
    308     }
    309 
    310     BuiltinAction InvokeMenuItem(int menu_position) {
    311         switch (menu_position) {
    312           case 0: return REBOOT;
    313           case 1: return APPLY_ADB_SIDELOAD;
    314           case 2: return WIPE_DATA;
    315           case 3: return WIPE_CACHE;
    316           default: return NO_ACTION;
    317         }
    318     }
    319 
    320     const char* const* GetMenuHeaders() { return HEADERS; }
    321     const char* const* GetMenuItems() { return ITEMS; }
    322 
    323   private:
    324     RecoveryUI* ui;
    325 };
    326 
    327 Device* make_device() {
    328     return new FuguDevice();
    329 }
    330