#include "device.h" #include #include #include #include #include #include #include #include #include #include #include /* ================================================================ * Unified init / deinit * ================================================================ */ void device_init(void) { if (fb_open() < 0) exit(1); if (ts_open() < 0) exit(1); beep_open(); led_open(); } void device_deinit(void) { led_close(); beep_close(); ts_close(); fb_close(); } /* ================================================================ * Globals * ================================================================ */ static int fb_fd = -1; static int fb_w = 0; static int fb_h = 0; static int fb_line = 0; static long fb_size = 0; static char *fb_map = NULL; static int ts_fd = -1; static int beep_fd = -1; /* ================================================================ * Framebuffer (/dev/fb0) * ================================================================ */ int fb_open(void) { fb_fd = open("/dev/fb0", O_RDWR); if (fb_fd < 0) { perror("open /dev/fb0"); return -1; } struct fb_var_screeninfo vi; struct fb_fix_screeninfo fi; ioctl(fb_fd, FBIOGET_VSCREENINFO, &vi); ioctl(fb_fd, FBIOGET_FSCREENINFO, &fi); vi.yoffset = 0; ioctl(fb_fd, FBIOPAN_DISPLAY, &vi); fb_w = vi.xres; fb_h = vi.yres; fb_line = fi.line_length; fb_size = fi.smem_len; fb_map = (char *)mmap(0, fb_size, PROT_READ | PROT_WRITE, MAP_SHARED, fb_fd, 0); if (fb_map == MAP_FAILED) { perror("mmap"); close(fb_fd); fb_fd = -1; return -1; } memset(fb_map, 0, fb_size); return 0; } void fb_close(void) { if (fb_map) { memset(fb_map, 0, fb_size); munmap(fb_map, fb_size); fb_map = NULL; } if (fb_fd >= 0) { close(fb_fd); fb_fd = -1; } } void fb_reset(void) { if (fb_fd < 0) return; struct fb_var_screeninfo vi; struct fb_fix_screeninfo fi; ioctl(fb_fd, FBIOGET_VSCREENINFO, &vi); ioctl(fb_fd, FBIOGET_FSCREENINFO, &fi); vi.yoffset = 0; ioctl(fb_fd, FBIOPAN_DISPLAY, &vi); fb_w = vi.xres; fb_h = vi.yres; fb_line = fi.line_length; fb_size = fi.smem_len; memset(fb_map, 0, fb_size); } void fb_put(int x, int y, unsigned int color) { if (x < 0 || x >= fb_w || y < 0 || y >= fb_h) return; unsigned int off = y * fb_line + x * 4; *(unsigned int *)(fb_map + off) = color; } void fb_fill(int x, int y, int w, int h, unsigned int color) { if (x < 0) { w += x; x = 0; } if (y < 0) { h += y; y = 0; } if (x + w > fb_w) w = fb_w - x; if (y + h > fb_h) h = fb_h - y; if (w <= 0 || h <= 0) return; int row, col; for (row = 0; row < h; row++) { unsigned int off = (y + row) * fb_line + x * 4; for (col = 0; col < w; col++) *(unsigned int *)(fb_map + off + col * 4) = color; } } void fb_fill_rounded(int x, int y, int w, int h, int r, unsigned int color) { if (r <= 0) { fb_fill(x, y, w, h, color); return; } if (r > w / 2) r = w / 2; if (r > h / 2) r = h / 2; /* Center + four edge rectangles */ fb_fill(x + r, y, w - 2 * r, h, color); fb_fill(x, y + r, r, h - 2 * r, color); fb_fill(x + w - r, y + r, r, h - 2 * r, color); /* Four corner quarter-circles */ int dx, dy, rr = r * r; for (dy = 0; dy < r; dy++) { for (dx = 0; dx < r; dx++) { if (dx * dx + dy * dy > rr) continue; int cx_tl = x + r - 1 - dx; int cy_tl = y + r - 1 - dy; fb_put(cx_tl, cy_tl, color); /* top-left */ fb_put(x + w - r + dx, cy_tl, color); /* top-right */ fb_put(cx_tl, y + h - r + dy, color); /* bottom-left */ fb_put(x + w - r + dx, y + h - r + dy, color); /* bottom-right */ } } } void fb_clear(void) { if (fb_map) memset(fb_map, 0, fb_size); } int fb_get_w(void) { return fb_w; } int fb_get_h(void) { return fb_h; } /* ================================================================ * Touchscreen (/dev/input/event0) * ================================================================ */ int ts_open(void) { ts_fd = open("/dev/input/event0", O_RDONLY | O_NONBLOCK); if (ts_fd < 0) { perror("open /dev/input/event0"); return -1; } return 0; } void ts_close(void) { if (ts_fd >= 0) { close(ts_fd); ts_fd = -1; } } void ts_raw_to_screen(int raw_x, int raw_y, int *sx, int *sy) { *sx = raw_x * SCREEN_W / TS_RAW_W; *sy = (raw_y - TS_Y_OFF) * SCREEN_H / (TS_RAW_H - TS_Y_OFF); if (*sx < 0) *sx = 0; if (*sx >= SCREEN_W) *sx = SCREEN_W - 1; if (*sy < 0) *sy = 0; if (*sy >= SCREEN_H) *sy = SCREEN_H - 1; } int ts_read(int *sx, int *sy, int *touching) { struct input_event ev; static int raw_x = 0; static int raw_y = 0; static int down = 0; static int has_new = 0; has_new = 0; while (read(ts_fd, &ev, sizeof(ev)) == sizeof(ev)) { if (ev.type == EV_ABS) { if (ev.code == ABS_X) raw_x = ev.value; if (ev.code == ABS_Y) raw_y = ev.value; } if (ev.type == EV_KEY && ev.code == BTN_TOUCH) { down = ev.value; ts_raw_to_screen(raw_x, raw_y, sx, sy); if (down) { *touching = 1; has_new = 1; } else { *touching = 0; has_new = 1; } if (has_new) return 1; } if (ev.type == EV_SYN && ev.code == SYN_REPORT && down) { ts_raw_to_screen(raw_x, raw_y, sx, sy); *touching = 1; has_new = 1; if (has_new) return 1; } } return has_new; } int get_touch_dir(void) { static int start_x = -1, start_y = -1; static int cur_x = 0, cur_y = 0; struct input_event ev; static int raw_x, raw_y; while (ts_fd >= 0) { int n = read(ts_fd, &ev, sizeof(ev)); if (n != sizeof(ev)) { if (n < 0 && errno == EAGAIN) break; break; } if (ev.type == EV_ABS) { if (ev.code == ABS_X) raw_x = ev.value; if (ev.code == ABS_Y) raw_y = ev.value; } if (ev.type == EV_KEY && ev.code == BTN_TOUCH) { if (ev.value == 1) { ts_raw_to_screen(raw_x, raw_y, &start_x, &start_y); cur_x = start_x; cur_y = start_y; } else { ts_raw_to_screen(raw_x, raw_y, &cur_x, &cur_y); int dx = cur_x - start_x; int dy = cur_y - start_y; int ax = abs(dx); int ay = abs(dy); start_x = -1; if (ax < SWIPE_MIN_DIST && ay < SWIPE_MIN_DIST) return 0; if (ax > ay * 2) return dx > 0 ? 4 : 3; /* RIGHT=4 : LEFT=3 */ if (ay > ax * 2) return dy > 0 ? 2 : 1; /* DOWN=2 : UP=1 */ return 0; } } if (ev.type == EV_SYN && ev.code == SYN_REPORT && start_x >= 0) { ts_raw_to_screen(raw_x, raw_y, &cur_x, &cur_y); } } return 0; } /* ================================================================ * BMP loader * ================================================================ */ unsigned int *bmp_load(const char *filename, int *w, int *h) { int fd = open(filename, O_RDONLY); if (fd < 0) { printf("bmp_load: cannot open %s\n", filename); return NULL; } struct stat st; fstat(fd, &st); char *raw = malloc(st.st_size); read(fd, raw, st.st_size); close(fd); int data_off = *(int *)(raw + 10); int bmp_w = *(int *)(raw + 18); int bmp_h = abs(*(int *)(raw + 22)); int bpp = *(short *)(raw + 28); int row_size = ((bmp_w * bpp + 31) / 32) * 4; printf("bmp_load: %dx%d @ %dbpp\n", bmp_w, bmp_h, bpp); unsigned int *pixels = malloc(bmp_w * bmp_h * 4); int y; for (y = 0; y < bmp_h; y++) { int bmp_y = bmp_h - 1 - y; unsigned char *row = (unsigned char *)(raw + data_off + bmp_y * row_size); int x; for (x = 0; x < bmp_w; x++) { int stride = (bpp == 32) ? 4 : 3; unsigned char b = row[x * stride + 0]; unsigned char g = row[x * stride + 1]; unsigned char r = row[x * stride + 2]; unsigned char a = (bpp == 32) ? row[x * stride + 3] : 255; pixels[y * bmp_w + x] = ((unsigned int)a << 24) | RGB(r, g, b); } } free(raw); *w = bmp_w; *h = bmp_h; return pixels; } void show_bmp(unsigned int *pixels, int w, int h, int x0, int y0) { /* Pre-calc visible X range — one bounds check per row, not per pixel */ int x_start = 0, x_end = w; if (x0 < 0) x_start = -x0; if (x0 + w > fb_w) x_end = fb_w - x0; if (x_start >= x_end) return; int y; for (y = 0; y < h; y++) { int sy = y0 + y; if (sy < 0 || sy >= fb_h) continue; unsigned int *src = pixels + y * w; unsigned int *dst = (unsigned int *)(fb_map + sy * fb_line + (x0 + x_start) * 4); int x; for (x = x_start; x < x_end; x++) { unsigned int c = src[x]; if ((c >> 24) == 0) continue; /* skip fully transparent */ dst[x - x_start] = c & 0x00FFFFFF; } } } void bmp_display(unsigned int *pixels, int w, int h) { int x0 = (fb_w - w) / 2; int y0 = (fb_h - h) / 2; int y; for (y = 0; y < h; y++) { int sy = y0 + y; if (sy < 0 || sy >= fb_h) continue; unsigned int *src = pixels + y * w; unsigned int *dst = (unsigned int *)(fb_map + sy * fb_line + x0 * 4); int x; for (x = 0; x < w; x++) dst[x] = src[x] & 0x00FFFFFF; /* strip alpha */ } } unsigned int *bmp_reshape(unsigned int *src, int sw, int sh, int dw, int dh) { unsigned int *dst = malloc(dw * dh * 4); int y, x; for (y = 0; y < dh; y++) { int sy = y * sh / dh; for (x = 0; x < dw; x++) { int sx = x * sw / dw; dst[y * dw + x] = src[sy * sw + sx]; } } return dst; } /* ================================================================ * Font rendering (monochrome bitmap, 8 pixels per byte) * ================================================================ */ void show_char(const unsigned char *font, int idx, int fw, int fh, int x0, int y0, unsigned int color) { int bytes_per_col = (fh + 7) / 8; int char_bytes = bytes_per_col * fw; const unsigned char *data = font + idx * char_bytes; int row, col; for (row = 0; row < fh; row++) { for (col = 0; col < fw; col++) { int byte_idx = col * bytes_per_col + row / 8; int bit_idx = row % 8; if (data[byte_idx] & (1 << bit_idx)) { fb_put(x0 + col, y0 + row, color); } } } } void show_char_scale(const unsigned char *font, int idx, int fw, int fh, int x0, int y0, int dw, int dh, unsigned int color) { int bytes_per_col = (fh + 7) / 8; int char_bytes = bytes_per_col * fw; const unsigned char *data = font + idx * char_bytes; int row, col; for (row = 0; row < dh; row++) { int src_row = row * fh / dh; for (col = 0; col < dw; col++) { int src_col = col * fw / dw; int byte_idx = src_col * bytes_per_col + src_row / 8; int bit_idx = src_row % 8; if (data[byte_idx] & (1 << bit_idx)) { fb_put(x0 + col, y0 + row, color); } } } } void show_number(const unsigned char *num_font, int stride, int num, int fw, int fh, int x0, int y0, int dw, int dh, unsigned int color) { char buf[16]; sprintf(buf, "%d", num); int i; for (i = 0; buf[i]; i++) { int d = buf[i] - '0'; show_char_scale(num_font + d * stride, 0, fw, fh, x0 + i * dw, y0, dw, dh, color); } } static int ascii_to_glyph(char c) { if (c >= 'A' && c <= 'Z') return c - 'A'; if (c >= 'a' && c <= 'z') return c - 'a'; if (c >= '0' && c <= '9') return c - '0' + 26; return -1; } void show_string(const unsigned char *font, int fw, int fh, const char *str, int x0, int y0, int dw, int dh, unsigned int color) { if (!str || !font) return; int i; for (i = 0; str[i]; i++) { int idx = ascii_to_glyph(str[i]); if (idx >= 0) show_char_scale(font, idx, fw, fh, x0 + i * dw, y0, dw, dh, color); } } /* ================================================================ * Buzzer (sysfs: /sys/kernel/gec_ctrl/beep, expects 4-byte write) * ================================================================ */ int beep_open(void) { beep_fd = open("/sys/kernel/gec_ctrl/beep", O_WRONLY); if (beep_fd < 0) { perror("open /sys/kernel/gec_ctrl/beep"); return -1; } return 0; } void beep_close(void) { if (beep_fd >= 0) { beep_off(); close(beep_fd); beep_fd = -1; } } void beep_on(void) { if (beep_fd < 0) return; int v = 1; lseek(beep_fd, 0, SEEK_SET); write(beep_fd, &v, sizeof(v)); } void beep_off(void) { if (beep_fd < 0) return; int v = 0; lseek(beep_fd, 0, SEEK_SET); write(beep_fd, &v, sizeof(v)); } void beep_freq(int hz) { (void)hz; beep_on(); } void beep_note(int hz, int ms) { (void)hz; beep_on(); usleep(ms * 1000); beep_off(); usleep(20 * 1000); } /* ================================================================ * LED (standard Linux LED class: /sys/class/leds/ledN/brightness) * ================================================================ */ static int led_fds[6]; /* 0=all, 1-5 = led1..led5 */ static const char *led_path(int n) { static char buf[64]; if (n <= 0) snprintf(buf, sizeof(buf), "/sys/class/leds/led1/brightness"); else snprintf(buf, sizeof(buf), "/sys/class/leds/led%d/brightness", n); return buf; } int led_open(void) { int i; for (i = 1; i <= 5; i++) { led_fds[i] = open(led_path(i), O_WRONLY); if (led_fds[i] < 0) perror(led_path(i)); } return led_fds[1] >= 0 ? 0 : -1; } void led_close(void) { int i; for (i = 1; i <= 5; i++) { if (led_fds[i] >= 0) { close(led_fds[i]); led_fds[i] = -1; } } } void led_on(int n) { int i; if (n <= 0 || n > 5) { for (i = 1; i <= 5; i++) led_on(i); return; } if (led_fds[n] < 0) return; lseek(led_fds[n], 0, SEEK_SET); write(led_fds[n], "1", 1); } void led_off(int n) { int i; if (n <= 0 || n > 5) { for (i = 1; i <= 5; i++) led_off(i); return; } if (led_fds[n] < 0) return; lseek(led_fds[n], 0, SEEK_SET); write(led_fds[n], "0", 1); }