1 /* 2 * Display the physical keypad matrix layout for the Letux 400 notebook 3 * computer. 4 * 5 * Copyright (C) 2018, 2023 Paul Boddie <paul@boddie.org.uk> 6 * 7 * This program is free software; you can redistribute it and/or 8 * modify it under the terms of the GNU General Public License as 9 * published by the Free Software Foundation; either version 2 of 10 * the License, or (at your option) any later version. 11 * 12 * This program is distributed in the hope that it will be useful, 13 * but WITHOUT ANY WARRANTY; without even the implied warranty of 14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 * GNU General Public License for more details. 16 * 17 * You should have received a copy of the GNU General Public License 18 * along with this program; if not, write to the Free Software 19 * Foundation, Inc., 51 Franklin Street, Fifth Floor, 20 * Boston, MA 02110-1301, USA 21 */ 22 23 #include <l4/re/c/rm.h> 24 #include <l4/re/c/util/cap_alloc.h> 25 #include <l4/re/env.h> 26 #include <l4/util/util.h> 27 #include <l4/sys/ipc.h> 28 29 #include <l4/re/c/util/video/goos_fb.h> 30 #include <l4/re/c/video/view.h> 31 32 #include <stdio.h> 33 #include <stdint.h> 34 #include <stdlib.h> 35 #include <unistd.h> 36 37 enum Jz4730_keypad_gpio 38 { 39 Jz4730_keypad_gpio_inputs_count = 8, 40 Jz4730_keypad_gpio_outputs_count = 17, 41 }; 42 43 /* Video abstractions. */ 44 45 static l4re_util_video_goos_fb_t gfb; 46 static l4re_video_view_info_t fbi; 47 static void *fb; 48 49 /* Keypad status and dimensions. */ 50 51 uint32_t *keypad = 0; 52 void *keymem = 0; 53 int columns = Jz4730_keypad_gpio_outputs_count, rows = Jz4730_keypad_gpio_inputs_count; 54 55 /* Position units: 0.1mm */ 56 57 enum { 58 SPC = 600, CAPS = 240, TAB = 200, LSH = 184, M = 160, S = 136, XS = 120, 59 WIDTH = 2040, HEIGHT = 800, ROWS = 6, 60 }; 61 62 /* 63 S(15) Esc F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 Num Sys Ins Del 64 XS(1)/M(12) ` 1 2 3 4 5 6 7 8 9 0 - Bsp 65 TAB(1)/M(10)/XS(2) Tab Q W E R T Y U I O P = \ 66 CAPS(1)/M(9)/XS(1)/CAPS(1) Caps A S D F G H J K L ; Enter 67 LSH(1)/XS(1)/M(7)/XS(4)/S(1) Sh \ Z X C V B N M , . / Up Sh 68 XS(5)/SPC(1)/XS(7) Fn Ctr Zzz Alt Pau Space Mnu [ ] ' Lt Dn Rt 69 */ 70 71 enum { 72 SIZE_KEY_ESC = S, SIZE_KEY_F1 = S, SIZE_KEY_F2 = S, SIZE_KEY_F3 = S, SIZE_KEY_F4 = S, 73 SIZE_KEY_F5 = S, SIZE_KEY_F6 = S, SIZE_KEY_F7 = S, SIZE_KEY_F8 = S, SIZE_KEY_F9 = S, 74 SIZE_KEY_F10 = S, SIZE_KEY_NUMLOCK = S, SIZE_KEY_SYSRQ = S, SIZE_KEY_INSERT = S, 75 SIZE_KEY_DELETE = S, 76 77 SIZE_KEY_GRAVE = XS, SIZE_KEY_1 = M, SIZE_KEY_2 = M, SIZE_KEY_3 = M, SIZE_KEY_4 = M, 78 SIZE_KEY_5 = M, SIZE_KEY_6 = M, SIZE_KEY_7 = M, SIZE_KEY_8 = M, SIZE_KEY_9 = M, 79 SIZE_KEY_0 = M, SIZE_KEY_MINUS = M, SIZE_KEY_BACKSPACE = M, 80 81 SIZE_KEY_TAB = TAB, SIZE_KEY_Q = M, SIZE_KEY_W = M, SIZE_KEY_E = M, SIZE_KEY_R = M, 82 SIZE_KEY_T = M, SIZE_KEY_Y = M, SIZE_KEY_U = M, SIZE_KEY_I = M, SIZE_KEY_O = M, 83 SIZE_KEY_P = M, SIZE_KEY_EQUAL = XS, SIZE_KEY_RIGHTBACKSLASH = XS, 84 85 SIZE_KEY_CAPSLOCK = CAPS, SIZE_KEY_A = M, SIZE_KEY_S = M, SIZE_KEY_D = M, SIZE_KEY_F = M, 86 SIZE_KEY_G = M, SIZE_KEY_H = M, SIZE_KEY_J = M, SIZE_KEY_K = M, SIZE_KEY_L = M, 87 SIZE_KEY_SEMICOLON = XS, SIZE_KEY_ENTER = CAPS, 88 89 SIZE_KEY_LEFTSHIFT = LSH, SIZE_KEY_LEFTBACKSLASH = XS, SIZE_KEY_Z = M, SIZE_KEY_X = M, SIZE_KEY_C = M, 90 SIZE_KEY_V = M, SIZE_KEY_B = M, SIZE_KEY_N = M, SIZE_KEY_M = M, SIZE_KEY_COMMA = XS, 91 SIZE_KEY_DOT = XS, SIZE_KEY_SLASH = XS, SIZE_KEY_UP = XS, SIZE_KEY_RIGHTSHIFT = S, 92 93 SIZE_KEY_FN = XS, SIZE_KEY_LEFTCTRL = XS, SIZE_KEY_SLEEP = XS, SIZE_KEY_LEFTALT = XS, SIZE_KEY_PAUSE = XS, 94 SIZE_KEY_SPACE = SPC, SIZE_KEY_MENU = XS, SIZE_KEY_LEFTBRACE = XS, SIZE_KEY_RIGHTBRACE = XS, 95 SIZE_KEY_APOSTROPHE = XS, SIZE_KEY_LEFT = XS, SIZE_KEY_DOWN = XS, SIZE_KEY_RIGHT = XS, 96 }; 97 98 enum { 99 XPOS_KEY_ESC = 0, XPOS_KEY_F1 = S, XPOS_KEY_F2 = 2*S, XPOS_KEY_F3 = 3*S, XPOS_KEY_F4 = 4*S, 100 XPOS_KEY_F5 = 5*S, XPOS_KEY_F6 = 6*S, XPOS_KEY_F7 = 7*S, XPOS_KEY_F8 = 8*S, XPOS_KEY_F9 = 9*S, 101 XPOS_KEY_F10 = 10*S, XPOS_KEY_NUMLOCK = 11*S, XPOS_KEY_SYSRQ = 12*S, XPOS_KEY_INSERT = 13*S, 102 XPOS_KEY_DELETE = 14*S, 103 104 XPOS_KEY_GRAVE = 0, XPOS_KEY_1 = XS, XPOS_KEY_2 = XS+M, XPOS_KEY_3 = XS+2*M, XPOS_KEY_4 = XS+3*M, 105 XPOS_KEY_5 = XS+4*M, XPOS_KEY_6 = XS+5*M, XPOS_KEY_7 = XS+6*M, XPOS_KEY_8 = XS+7*M, XPOS_KEY_9 = XS+8*M, 106 XPOS_KEY_0 = XS+9*M, XPOS_KEY_MINUS = XS+10*M, XPOS_KEY_BACKSPACE = XS+11*M, 107 108 XPOS_KEY_TAB = 0, XPOS_KEY_Q = TAB, XPOS_KEY_W = TAB+M, XPOS_KEY_E = TAB+2*M, XPOS_KEY_R = TAB+3*M, 109 XPOS_KEY_T = TAB+4*M, XPOS_KEY_Y = TAB+5*M, XPOS_KEY_U = TAB+6*M, XPOS_KEY_I = TAB+7*M, XPOS_KEY_O = TAB+8*M, 110 XPOS_KEY_P = TAB+9*M, XPOS_KEY_EQUAL = TAB+10*M, XPOS_KEY_RIGHTBACKSLASH = TAB+10*M+XS, 111 112 XPOS_KEY_CAPSLOCK = 0, XPOS_KEY_A = CAPS, XPOS_KEY_S = CAPS+M, XPOS_KEY_D = CAPS+2*M, XPOS_KEY_F = CAPS+3*M, 113 XPOS_KEY_G = CAPS+4*M, XPOS_KEY_H = CAPS+5*M, XPOS_KEY_J = CAPS+6*M, XPOS_KEY_K = CAPS+7*M, XPOS_KEY_L = CAPS+8*M, 114 XPOS_KEY_SEMICOLON = CAPS+9*M, XPOS_KEY_ENTER = CAPS+9*M+XS, 115 116 XPOS_KEY_LEFTSHIFT = 0, XPOS_KEY_LEFTBACKSLASH = LSH, XPOS_KEY_Z = LSH+XS, XPOS_KEY_X = LSH+XS+M, XPOS_KEY_C = LSH+XS+2*M, 117 XPOS_KEY_V = LSH+XS+3*M, XPOS_KEY_B = LSH+XS+4*M, XPOS_KEY_N = LSH+XS+5*M, XPOS_KEY_M = LSH+XS+6*M, XPOS_KEY_COMMA = LSH+XS+7*M, 118 XPOS_KEY_DOT = LSH+XS+7*M+XS, XPOS_KEY_SLASH = LSH+XS+7*M+2*XS, XPOS_KEY_UP = LSH+XS+7*M+3*XS, XPOS_KEY_RIGHTSHIFT = LSH+XS+7*M+4*XS, 119 120 XPOS_KEY_FN = 0, XPOS_KEY_LEFTCTRL = XS, XPOS_KEY_SLEEP = 2*XS, XPOS_KEY_LEFTALT = 3*XS, XPOS_KEY_PAUSE = 4*XS, 121 XPOS_KEY_SPACE = 5*XS, XPOS_KEY_MENU = 5*XS+SPC, XPOS_KEY_LEFTBRACE = 5*XS+SPC+XS, XPOS_KEY_RIGHTBRACE = 5*XS+SPC+2*XS, 122 XPOS_KEY_APOSTROPHE = 5*XS+SPC+3*XS, XPOS_KEY_LEFT = 5*XS+SPC+4*XS, XPOS_KEY_DOWN = 5*XS+SPC+5*XS, XPOS_KEY_RIGHT = 5*XS+SPC+6*XS, 123 }; 124 125 enum { 126 YPOS_KEY_ESC = 0, YPOS_KEY_F1 = 0, YPOS_KEY_F2 = 0, YPOS_KEY_F3 = 0, YPOS_KEY_F4 = 0, 127 YPOS_KEY_F5 = 0, YPOS_KEY_F6 = 0, YPOS_KEY_F7 = 0, YPOS_KEY_F8 = 0, YPOS_KEY_F9 = 0, 128 YPOS_KEY_F10 = 0, YPOS_KEY_NUMLOCK = 0, YPOS_KEY_SYSRQ = 0, YPOS_KEY_INSERT = 0, 129 YPOS_KEY_DELETE = 0, 130 131 YPOS_KEY_GRAVE = 1, YPOS_KEY_1 = 1, YPOS_KEY_2 = 1, YPOS_KEY_3 = 1, YPOS_KEY_4 = 1, 132 YPOS_KEY_5 = 1, YPOS_KEY_6 = 1, YPOS_KEY_7 = 1, YPOS_KEY_8 = 1, YPOS_KEY_9 = 1, 133 YPOS_KEY_0 = 1, YPOS_KEY_MINUS = 1, YPOS_KEY_BACKSPACE = 1, 134 135 YPOS_KEY_TAB = 2, YPOS_KEY_Q = 2, YPOS_KEY_W = 2, YPOS_KEY_E = 2, YPOS_KEY_R = 2, 136 YPOS_KEY_T = 2, YPOS_KEY_Y = 2, YPOS_KEY_U = 2, YPOS_KEY_I = 2, YPOS_KEY_O = 2, 137 YPOS_KEY_P = 2, YPOS_KEY_EQUAL = 2, YPOS_KEY_RIGHTBACKSLASH = 2, 138 139 YPOS_KEY_CAPSLOCK = 3, YPOS_KEY_A = 3, YPOS_KEY_S = 3, YPOS_KEY_D = 3, YPOS_KEY_F = 3, 140 YPOS_KEY_G = 3, YPOS_KEY_H = 3, YPOS_KEY_J = 3, YPOS_KEY_K = 3, YPOS_KEY_L = 3, 141 YPOS_KEY_SEMICOLON = 3, YPOS_KEY_ENTER = 3, 142 143 YPOS_KEY_LEFTSHIFT = 4, YPOS_KEY_LEFTBACKSLASH = 4, YPOS_KEY_Z = 4, YPOS_KEY_X = 4, YPOS_KEY_C = 4, 144 YPOS_KEY_V = 4, YPOS_KEY_B = 4, YPOS_KEY_N = 4, YPOS_KEY_M = 4, YPOS_KEY_COMMA = 4, 145 YPOS_KEY_DOT = 4, YPOS_KEY_SLASH = 4, YPOS_KEY_UP = 4, YPOS_KEY_RIGHTSHIFT = 4, 146 147 YPOS_KEY_FN = 5, YPOS_KEY_LEFTCTRL = 5, YPOS_KEY_SLEEP = 5, YPOS_KEY_LEFTALT = 5, YPOS_KEY_PAUSE = 5, 148 YPOS_KEY_SPACE = 5, YPOS_KEY_MENU = 5, YPOS_KEY_LEFTBRACE = 5, YPOS_KEY_RIGHTBRACE = 5, 149 YPOS_KEY_APOSTROPHE = 5, YPOS_KEY_LEFT = 5, YPOS_KEY_DOWN = 5, YPOS_KEY_RIGHT = 5, 150 }; 151 152 /* Keypad matrix mapping. */ 153 154 #define PHYS_KEY(X) {XPOS_KEY_##X, YPOS_KEY_##X, SIZE_KEY_##X} 155 #define PHYS_NULL {0, 0, 0} 156 157 static const uint32_t keypos[Jz4730_keypad_gpio_inputs_count][Jz4730_keypad_gpio_outputs_count][3] = { 158 159 {PHYS_KEY(PAUSE), PHYS_KEY(Q), PHYS_KEY(W), PHYS_KEY(E), PHYS_KEY(R), PHYS_KEY(U), PHYS_KEY(I), PHYS_KEY(O), 160 PHYS_NULL, PHYS_NULL, PHYS_NULL, PHYS_KEY(P), PHYS_NULL, PHYS_NULL, PHYS_NULL, PHYS_NULL}, 161 162 {PHYS_NULL, PHYS_KEY(TAB), PHYS_KEY(CAPSLOCK), PHYS_KEY(F3), PHYS_KEY(T), PHYS_KEY(Y), PHYS_KEY(RIGHTBRACE), PHYS_KEY(F7), 163 PHYS_NULL, PHYS_KEY(BACKSPACE), PHYS_NULL, PHYS_KEY(LEFTBRACE), PHYS_KEY(SLEEP), PHYS_NULL, PHYS_NULL, PHYS_NULL, PHYS_KEY(LEFTSHIFT)}, 164 165 {PHYS_NULL, PHYS_KEY(A), PHYS_KEY(S), PHYS_KEY(D), PHYS_KEY(F), PHYS_KEY(J), PHYS_KEY(K), PHYS_KEY(L), 166 PHYS_NULL, PHYS_NULL, PHYS_NULL, PHYS_KEY(SEMICOLON), PHYS_NULL, PHYS_NULL, PHYS_NULL, PHYS_KEY(UP), PHYS_KEY(RIGHTSHIFT)}, 167 168 {PHYS_NULL, PHYS_KEY(ESC), PHYS_KEY(LEFTBACKSLASH), PHYS_KEY(F4), PHYS_KEY(G), PHYS_KEY(H), PHYS_KEY(F6), PHYS_NULL, 169 PHYS_KEY(SPACE), PHYS_NULL, PHYS_KEY(LEFTALT), PHYS_KEY(APOSTROPHE), PHYS_NULL, PHYS_NULL, PHYS_NULL, PHYS_KEY(DOWN), PHYS_NULL}, 170 171 {PHYS_NULL, PHYS_KEY(Z), PHYS_KEY(X), PHYS_KEY(C), PHYS_KEY(V), PHYS_KEY(M), PHYS_KEY(COMMA), PHYS_KEY(DOT), 172 PHYS_KEY(NUMLOCK), PHYS_KEY(ENTER), PHYS_NULL, PHYS_KEY(RIGHTBACKSLASH), PHYS_NULL, PHYS_NULL, PHYS_NULL, PHYS_KEY(LEFT), PHYS_NULL}, 173 174 {PHYS_NULL, PHYS_NULL, PHYS_NULL, PHYS_NULL, PHYS_KEY(B), PHYS_KEY(N), PHYS_NULL, PHYS_KEY(MENU), 175 PHYS_NULL, PHYS_NULL, PHYS_NULL, PHYS_KEY(SLASH), PHYS_NULL, PHYS_NULL, PHYS_NULL, PHYS_KEY(RIGHT), PHYS_NULL}, 176 177 {PHYS_KEY(LEFTCTRL), PHYS_KEY(GRAVE), PHYS_NULL, PHYS_NULL, PHYS_KEY(5), PHYS_KEY(6), PHYS_KEY(EQUAL), PHYS_KEY(F8), 178 PHYS_KEY(DELETE), PHYS_KEY(F9), PHYS_NULL, PHYS_KEY(MINUS), PHYS_NULL, PHYS_KEY(F2), PHYS_KEY(INSERT), PHYS_NULL, PHYS_KEY(F1)}, 179 180 {PHYS_KEY(F5), PHYS_KEY(1), PHYS_KEY(2), PHYS_KEY(3), PHYS_KEY(4), PHYS_KEY(7), PHYS_KEY(8), PHYS_KEY(9), 181 PHYS_NULL, PHYS_NULL, PHYS_KEY(SYSRQ), PHYS_KEY(0), PHYS_KEY(F10), PHYS_NULL, PHYS_NULL, PHYS_NULL, PHYS_KEY(FN)} 182 }; 183 184 185 186 static uint32_t bitmask(uint32_t size) 187 { 188 return (1 << size) - 1; 189 } 190 191 static uint32_t truncate_channel(uint32_t value, uint32_t size) 192 { 193 return (value >> (8 - size)) & bitmask(size); 194 } 195 196 static void set_pixel(uint32_t pos, uint8_t bpp, uint32_t value) 197 { 198 if (bpp <= 8) *(uint8_t *) pos = value; 199 else if (bpp <= 16) *(uint16_t *) pos = value; 200 else *(uint32_t *) pos = value; 201 } 202 203 /* Show the state of a key on the display. */ 204 205 static void fill_rectangle(uint32_t x, uint32_t y, uint32_t w, uint32_t h, uint32_t rgb) 206 { 207 uint8_t bpp = l4re_video_bits_per_pixel(&fbi.pixel_info); 208 uint8_t bytes_per_pixel = fbi.pixel_info.bytes_per_pixel; 209 uint32_t bytes_per_line = fbi.bytes_per_line; 210 uint32_t pos; 211 uint32_t col, row; 212 213 rgb = (truncate_channel(rgb >> 16, fbi.pixel_info.r.size) << fbi.pixel_info.r.shift) | 214 (truncate_channel(rgb >> 8, fbi.pixel_info.g.size) << fbi.pixel_info.g.shift) | 215 (truncate_channel(rgb, fbi.pixel_info.b.size) << fbi.pixel_info.b.shift); 216 217 for (row = 0; row < h; row++) 218 { 219 pos = (uint32_t) fb + (y + row) * bytes_per_line + x * bytes_per_pixel; 220 221 for (col = 0; col < w; col++) 222 { 223 set_pixel(pos, bpp, rgb); 224 pos += bytes_per_pixel; 225 } 226 } 227 } 228 229 /* Show the keypad status on the display. */ 230 231 static void show_keypad(void) 232 { 233 uint32_t rowsize = fbi.height / ROWS, colsize; 234 uint8_t column, row; 235 uint32_t mask; 236 const uint32_t *pos; 237 238 for (column = 0; column < columns; column++) 239 240 for (row = 0, mask = 1 << (rows - 1); 241 row < rows; 242 row++, mask >>= 1) 243 { 244 /* Obtain the physical position. */ 245 246 pos = keypos[row][column]; 247 248 /* Obtain the width of the key. */ 249 250 colsize = (pos[2] * fbi.width) / WIDTH; 251 252 /* Plot the rectangle for the key. */ 253 254 fill_rectangle((pos[0] * fbi.width) / WIDTH, pos[1] * rowsize, colsize, rowsize, 255 keypad[column] & mask ? 0xffffff : 0); 256 } 257 258 /* Refresh the display. */ 259 260 l4re_util_video_goos_fb_refresh(&gfb, 0, 0, fbi.width, fbi.height); 261 } 262 263 264 265 int main(void) 266 { 267 l4_cap_idx_t keypad_server; 268 l4re_ds_t mem; 269 l4_msgtag_t tag; 270 271 if (l4re_util_video_goos_fb_setup_name(&gfb, "fb")) 272 return 1; 273 274 if (l4re_util_video_goos_fb_view_info(&gfb, &fbi)) 275 return 1; 276 277 if (!(fb = l4re_util_video_goos_fb_attach_buffer(&gfb))) 278 return 1; 279 280 /* Obtain a reference to the keypad. */ 281 282 keypad_server = l4re_env_get_cap("keypad"); 283 if (l4_is_invalid_cap(keypad_server)) return 1; 284 285 /* Obtain a capability for the keypad data. */ 286 287 mem = l4re_util_cap_alloc(); 288 if (l4_is_invalid_cap(mem)) return 1; 289 290 /* Obtain a reference to the keypad data. */ 291 292 l4_utcb_br()->bdr = 0; 293 l4_utcb_br()->br[0] = L4_RCV_ITEM_SINGLE_CAP | mem; 294 295 tag = l4_ipc_call(keypad_server, l4_utcb(), 296 l4_msgtag(0, 0, 0, 0), /* label zero, zero words, zero *sent* items */ 297 L4_IPC_NEVER); 298 299 if (l4_ipc_error(tag, l4_utcb())) return 1; 300 301 /* Attach the keypad data to a region in this task. */ 302 303 if (l4re_rm_attach(&keymem, l4re_ds_size(mem), L4RE_RM_F_SEARCH_ADDR, mem, 0, 304 L4_PAGESHIFT)) 305 return 1; 306 307 /* Show the keypad state. */ 308 309 keypad = (uint32_t *) keymem; 310 311 while (1) show_keypad(); 312 313 return 0; 314 }