5 * This work is licensed under the terms of 3-clause BSD license.
6 * See COPYING file in the top-level directory.
15 #include <sys/types.h>
18 #include <sys/ioctl.h>
23 #include <sys/socket.h>
28 #include <linux/omapfb.h>
32 #define SCREEN_WIDTH 800
33 #define SCREEN_HEIGHT 480
38 static struct fb_var_screeninfo g_vi;
39 static uint16_t *g_screen_base, *g_screen;
43 static unsigned int g_mem_size;
44 static unsigned int g_old_mem;
46 static int g_exit, g_hide;
47 static pthread_cond_t g_cond;
49 #define ARRAY_SIZE(x) (sizeof(x) / sizeof(x[0]))
50 #define PAGE_ALIGN(x) (((x) + 0xfff) & ~0xfff)
51 #define IS_START(buf, str) \
52 !strncmp(buf, str, sizeof(str) - 1)
54 static void clear_bytes(void *mem, int bytes);
56 static int setup_layer(int fd)
58 struct omapfb_plane_info pi;
59 struct omapfb_mem_info mi;
60 struct omapfb_color_key key;
63 ret = ioctl(fd, OMAPFB_QUERY_PLANE, &pi);
65 perror("ioctl OMAPFB_QUERY_PLANE");
69 ret = ioctl(fd, OMAPFB_QUERY_MEM, &mi);
71 perror("ioctl OMAPFB_QUERY_MEM");
75 /* must disable when changing stuff */
78 ret = ioctl(fd, OMAPFB_SETUP_PLANE, &pi);
80 perror("ioctl OMAPFB_SETUP_PLANE (d)");
86 g_mem_size = PAGE_ALIGN(g_layer.w * g_layer.h * 2 * 2);
87 if (mi.size < g_mem_size) {
89 ret = ioctl(fd, OMAPFB_SETUP_MEM, &mi);
91 perror("ioctl SETUP_MEM");
96 printf("layer: %d %d %d %d\n",
97 g_layer.x, g_layer.y, g_layer.w, g_layer.h);
100 pi.pos_y = g_layer.y;
101 pi.out_width = g_layer.w;
102 pi.out_height = g_layer.h;
103 ret = ioctl(fd, OMAPFB_SETUP_PLANE, &pi);
105 perror("ioctl OMAPFB_SETUP_PLANE (e1)");
109 ret = ioctl(fd, FBIOGET_VSCREENINFO, &g_vi);
111 perror("ioctl FBIOGET_VSCREENINFO");
115 g_vi.xres = g_vi.xres_virtual = g_layer.w;
116 g_vi.yres = g_layer.h;
117 g_vi.yres_virtual = g_layer.h * 2;
118 g_vi.bits_per_pixel = 16;
120 ret = ioctl(fd, FBIOPUT_VSCREENINFO, &g_vi);
122 perror("ioctl FBIOPUT_VSCREENINFO");
127 ret = ioctl(fd, OMAPFB_SETUP_PLANE, &pi);
129 perror("ioctl OMAPFB_SETUP_PLANE (e2)");
133 memset(&key, 0, sizeof(key));
134 key.key_type = OMAPFB_COLOR_KEY_VID_SRC;
135 key.trans_key = 0x07e0;
137 ret = ioctl(fd, OMAPFB_SET_COLOR_KEY, &key);
139 perror("ioctl OMAPFB_SET_COLOR_KEY");
143 g_screen_base = mmap(NULL, g_mem_size, PROT_READ | PROT_WRITE,
145 if (g_screen_base == MAP_FAILED) {
147 g_screen = g_screen_base = NULL;
150 clear_bytes(g_screen_base, g_mem_size);
151 g_screen = g_screen_base;
157 static int check_layer(int fd)
159 struct omapfb_plane_info pi;
162 ret = ioctl(fd, OMAPFB_QUERY_PLANE, &pi);
164 perror("ioctl OMAPFB_QUERY_PLANE");
169 printf("something disabled the layer (tv-out?), quitting\n");
176 static void remove_layer(int fd)
178 struct omapfb_plane_info pi;
179 struct omapfb_mem_info mi;
182 if (g_screen_base != NULL) {
183 munmap(g_screen_base, g_mem_size);
184 g_screen = g_screen_base = NULL;
187 memset(&pi, 0, sizeof(pi));
189 ret = ioctl(fd, OMAPFB_SETUP_PLANE, &pi);
191 perror("ioctl OMAPFB_SETUP_PLANE (c)");
193 ret = ioctl(fd, OMAPFB_QUERY_MEM, &mi);
195 perror("ioctl OMAPFB_QUERY_MEM");
199 if (mi.size != g_old_mem) {
201 ret = ioctl(fd, OMAPFB_SETUP_MEM, &mi);
203 perror("ioctl SETUP_MEM");
211 static void flip_fb(int fd)
213 g_vi.yoffset = g_flip_id * g_layer.h;
214 ioctl(fd, FBIOPAN_DISPLAY, &g_vi);
216 g_screen = g_screen_base + g_flip_id * g_layer.w * g_layer.h;
219 static void handle_term(int num)
222 pthread_cond_signal(&g_cond);
225 static void handle_usr1(int num)
228 pthread_cond_signal(&g_cond);
236 static int handle_socket(int sock)
241 ret = recv(sock, buf, sizeof(buf) - 1, 0);
250 p = strchr(buf, ':');
252 for (i = 0; i < ARRAY_SIZE(user_msgs); i++) {
253 if (user_msgs[i].text[0] == 0)
255 if (!strncmp(user_msgs[i].text, buf, p - buf + 1))
258 if (i == ARRAY_SIZE(user_msgs)) {
259 printf("out of user_msg slots\n");
262 memcpy(user_msgs[i].text, buf, sizeof(user_msgs[i].text) - 1);
263 user_msgs[i].text[sizeof(user_msgs[i].text) - 1] = 0;
264 user_msgs[i].idle = 0;
266 else if (!strcmp(buf, "poke")) {
267 // if hidden, show up
276 else if (!strcmp(buf, "hide")) {
280 else if (!strcmp(buf, "show")) {
284 else if (!strcmp(buf, "quit")) {
289 printf("unknown command: '%s'\n", buf);
295 static void default_config(void)
297 g_layer.x = SCREEN_WIDTH - WIDTH;
303 static void handle_config(void)
311 f = fopen("config.cfg", "r");
315 while ((p = fgets(buf, sizeof(buf), f))) {
318 if (*p == '#' || *p == ';' || *p == 0)
321 if (sscanf(p, "position = %d %d", &x, &y) == 2) {
326 printf("unhandled config entry: '%s'\n", p);
331 v = SCREEN_WIDTH - g_layer.x;
333 printf("bad x position in config: %d\n", g_layer.x);
340 v = SCREEN_HEIGHT - g_layer.y;
342 printf("bad y position in config: %d\n", g_layer.y);
350 #define s_printf(x, y, fmt, ...) do { \
351 if ((y) <= g_layer.h - 8) \
352 basic_text_out16(g_screen, g_layer.w, x, y, fmt, ##__VA_ARGS__); \
355 static int read_int_file(const char *fn, int *val)
364 ret = fscanf(f, "%d", val);
372 static void clear_bytes(void *mem, int bytes)
377 for (; l >= 4; l -= 4, p += 4)
378 p[0] = p[1] = p[2] = p[3] = 0x07e007e0;
379 for (; l > 0; l--, p++)
383 static void clear(int h)
388 clear_bytes(g_screen, g_layer.w * h * 2);
392 unsigned long idlesum;
399 struct proc_stat last_st;
409 static void collect_proc_stat(void)
415 unsigned long iowait;
419 hz = sysconf(_SC_CLK_TCK);
420 printf("hz: %ld\n", hz);
423 f = fopen("/proc/stat", "r");
427 if (fscanf(f, "cpu %*s %*s %lu %lu %lu",
428 &st.sys, &wait, &iowait) != 3)
431 if (fgets(buf, sizeof(buf), f) == NULL)
433 } while (fscanf(f, "intr %lu", &st.irqs) != 1);
435 if (fgets(buf, sizeof(buf), f) == NULL)
437 } while (fscanf(f, "ctxt %lu", &st.ctxt) != 1);
439 st.idlesum = wait + iowait;
441 stats.cpu_usage = hz - (st.idlesum - stats.last_st.idlesum);
442 stats.cpu_sys = st.sys - stats.last_st.sys;
444 stats.cpu_usage = stats.cpu_usage * 100 / hz;
445 stats.cpu_sys = stats.cpu_sys * 100 / hz;
447 stats.irqs = st.irqs - stats.last_st.irqs;
448 stats.ctxt = st.ctxt - stats.last_st.ctxt;
455 static void collect_mem_stat(void)
457 static const char fn[] = "/proc/meminfo";
458 uint32_t total = 0, free = 0, buffers = 0, cached = 0;
459 uint32_t anonhuge = 0, hugetot = 0, hugefree = 0, hugepgsz = 0;
460 uint32_t free_swap = 0;
468 while (fgets(buf, sizeof(buf), f)) {
469 if (total == 0 && sscanf(buf, "MemTotal: %u", &total) == 1)
471 if (free == 0 && sscanf(buf, "MemFree: %u", &free) == 1)
473 if (buffers == 0 && sscanf(buf, "Buffers: %u", &buffers) == 1)
475 if (cached == 0 && sscanf(buf, "Cached: %u", &cached) == 1)
477 if (free_swap == 0 && sscanf(buf, "SwapFree: %u", &free_swap) == 1)
479 if (anonhuge == 0 && sscanf(buf, "AnonHugePages: %u", &anonhuge) == 1)
481 if (hugetot == 0 && sscanf(buf, "HugePages_Total: %u", &hugetot) == 1)
483 if (hugefree == 0 && sscanf(buf, "HugePages_Free: %u", &hugefree) == 1)
485 if (hugepgsz == 0 && sscanf(buf, "Hugepagesize: %u", &hugepgsz) == 1)
491 hugetot = (hugetot - hugefree) * hugepgsz;
492 free += buffers + cached;
493 stats.mem_free = free >> 10;
494 stats.mem_huge = hugetot >> 10;
495 stats.mem_free_swap = free_swap >> 10;
504 struct io_stat last_st;
509 static void collect_io_stat(void)
511 unsigned long sec_r, sec_w;
512 struct io_stat st = { 0, };
517 for (i = 0; i < 2; i++) {
518 snprintf(buf, sizeof(buf), "/sys/block/mmcblk%d/stat", i);
523 if (fscanf(f, "%*s %*s %lu %*s %*s %*s %lu",
524 &sec_r, &sec_w) == 2)
532 io_stats.read = st.sec_r - io_stats.last_st.sec_r;
533 io_stats.write = st.sec_w - io_stats.last_st.sec_w;
534 // assuming 512 byte sectors..
536 io_stats.write >>= 1;
538 io_stats.last_st = st;
547 struct net_stat last_st;
555 static void collect_net_stat(void)
557 struct net_stat st = { 0, };
558 unsigned long rx, tx;
563 net_stats.down = net_stats.up =
564 net_stats.have_wlan = net_stats.link = net_stats.level = 0;
566 f = fopen("/proc/net/dev", "r");
571 static const char fmt[] =
572 "%255s %lu %*s %*s %*s %*s %*s %*s %lu";
573 if (fscanf(f, fmt, buf, &rx, &tx) != 3)
575 if (IS_START(buf, "lo"))
584 net_stats.down = st.rx - net_stats.last_st.rx;
585 net_stats.up = st.tx - net_stats.last_st.tx;
587 net_stats.last_st = st;
589 f = fopen("/proc/net/wireless", "r");
595 if (fscanf(f, "%255s %*x %d. %d.", buf, &lnk, &lvl) != 3)
597 if (IS_START(buf, "lo"))
599 // first one should be enough
600 net_stats.have_wlan = 1;
601 net_stats.link = lnk;
602 net_stats.level = lvl;
608 static void collect_stats(void)
616 static char cpu_reg_path[64];
618 static void init_stats(void)
625 // find the CPU regulator
626 for (i = found = 0; ; i++) {
627 snprintf(buf, sizeof(buf),
628 "/sys/class/regulator/regulator.%d/name", i);
632 fgets(buf, sizeof(buf), f);
634 if (IS_START(buf, "vdd_mpu")) {
641 snprintf(cpu_reg_path, sizeof(cpu_reg_path),
642 "/sys/class/regulator/regulator.%d/microvolts", i);
644 // do collect so differential stats get sane values
648 static void get_time(int x, int y)
654 gettimeofday(&tv, NULL);
655 t = localtime(&tv.tv_sec);
656 strftime(buf, sizeof(buf), "%T", t);
657 s_printf(x, y, " %s", buf);
660 static void get_cpu(int x, int y)
662 s_printf(x, y, "CPU:%5d%%", stats.cpu_usage);
665 static void get_sys(int x, int y)
667 s_printf(x, y, "SYS:%5d%%", stats.cpu_sys);
670 static void get_irq(int x, int y)
672 s_printf(x, y, "IRQ:%5u", stats.irqs);
675 static void get_cs(int x, int y)
677 s_printf(x, y, "CS:%6u", stats.ctxt);
680 static void get_cpuf(int x, int y)
682 static const char fn[] =
683 "/sys/devices/system/cpu/cpu0/cpufreq/scaling_cur_freq";
686 if (read_int_file(fn, &f))
688 s_printf(x, y, "f:%7dM", f / 1000);
691 static void get_cpuv(int x, int y)
695 if (!cpu_reg_path[0] || read_int_file(cpu_reg_path, &v))
697 s_printf(x, y, "V:%7.3fV", (float)v / 1000000.0f);
700 static void get_memfree(int x, int y)
702 s_printf(x, y, "M:%7dM", stats.mem_free);
705 static void get_memhuge(int x, int y)
707 s_printf(x, y, "H:%7dM", stats.mem_huge);
710 static void get_memswap(int x, int y)
712 s_printf(x, y, "S:%7dM", stats.mem_free_swap);
715 #define PS_BASE "/sys/class/power_supply/bq27500-0/"
717 static void get_bcap(int x, int y)
719 static const char fnc[] = PS_BASE "capacity";
722 if (read_int_file(fnc, &c))
724 s_printf(x, y, "C:%7d%%", c);
727 static void get_btime(int x, int y)
729 static const char fne[] = PS_BASE "time_to_empty_now";
730 static const char fnf[] = PS_BASE "time_to_full_now";
733 if (read_int_file(fne, &t) || t == 0) {
734 if (read_int_file(fnf, &t))
737 s_printf(x, y, "t:%4d:%02d", t / 3600, t / 60 % 60);
740 static void get_bvolts(int x, int y)
742 static const char fnv[] = PS_BASE "voltage_now";
745 if (read_int_file(fnv, &volt))
748 s_printf(x, y, "V:%7.3fV", (float)volt / 1000000.0f);
751 static void get_bwatts(int x, int y)
753 static const char fnc[] = PS_BASE "current_now";
754 static const char fnv[] = PS_BASE "voltage_now";
755 int w, cur = 0, volt = 0;
757 if (read_int_file(fnc, &cur))
759 if (read_int_file(fnv, &volt))
762 w = (cur / 1000) * (volt / 1000);
763 s_printf(x, y, "P:%7.3fW", (float)w / 1000000.0f);
766 static void get_btemp(int x, int y)
768 static const char fnt[] = PS_BASE "temp";
771 if (read_int_file(fnt, &t))
773 s_printf(x, y, "T:%7.1fC", (float)t / 10.0f);
776 static void get_ioread(int x, int y)
778 s_printf(x, y, "R:%5.1fM/s", (float)io_stats.read / 1000.0f);
781 static void get_iowrite(int x, int y)
783 s_printf(x, y, "W:%5.1fM/s", (float)io_stats.write / 1000.0f);
786 static void get_netdown(int x, int y)
788 s_printf(x, y, "D:%5uK/s", net_stats.down / 1000);
791 static void get_netup(int x, int y)
793 s_printf(x, y, "U:%5uK/s", net_stats.up / 1000);
796 static void get_netsgnl(int x, int y)
798 s_printf(x, y, "S:%5ddBm", net_stats.level);
801 int main(int argc, char *argv[])
803 static const char socket_name[] = "\0liveinfo";
804 static const char fbname[] = "/dev/fb2";
805 pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
806 pthread_condattr_t condattr;
807 struct sockaddr_un sun;
812 int y = 0, y_max = 0;
817 // look for other instance
818 sock = socket(PF_UNIX, SOCK_DGRAM, 0);
820 perror("socket PF_UNIX");
824 memset(&sun, 0, sizeof(sun));
825 sun.sun_family = AF_UNIX;
826 memcpy(sun.sun_path, socket_name, sizeof(socket_name));
828 ret = connect(sock, (struct sockaddr *)&sun, sizeof(sun));
830 printf("other instance detected, sending poke command\n");
831 ret = send(sock, "poke", 4, 0);
835 return ret == 4 ? 0 : 1;
841 fd = open(fbname, O_RDWR);
843 fprintf(stderr, "open %s: ", fbname);
850 // no need for privileges any more
851 p = getenv("SUDO_UID");
861 // bind/listen for commands
862 ret = bind(sock, (struct sockaddr *)&sun, sizeof(sun));
870 ret = listen(sock, 1);
879 pfd.events = POLLIN | POLLPRI;
882 ret = pthread_condattr_init(&condattr);
883 ret |= pthread_condattr_setclock(&condattr, CLOCK_MONOTONIC);
884 ret |= pthread_cond_init(&g_cond, &condattr);
885 pthread_condattr_destroy(&condattr);
887 fprintf(stderr, "cond init failed\n");
891 ret = setup_layer(fd);
895 // clean up on kill too
896 signal(SIGTERM, handle_term);
897 signal(SIGUSR1, handle_usr1);
899 pthread_mutex_lock(&mutex);
900 ret = clock_gettime(CLOCK_MONOTONIC, &ts);
902 perror("clock_gettime CLOCK_MONOTONIC");
910 // anything on unix socket?
911 ret = poll(&pfd, 1, 0);
917 ret = handle_socket(sock);
931 else if (is_hidden) {
932 ret = setup_layer(fd);
938 if (check_layer(fd) != 0)
949 get_time (0, y += Y_STEP);
951 get_cpu (0, y += Y_STEP);
952 get_sys (0, y += Y_STEP);
953 get_irq (0, y += Y_STEP);
954 get_cs (0, y += Y_STEP);
955 get_cpuf (0, y += Y_STEP);
956 get_cpuv (0, y += Y_STEP);
958 s_printf(0, y += Y_STEP, "mem");
959 get_memfree(0, y += Y_STEP);
960 get_memswap(0, y += Y_STEP);
962 get_memhuge(0, y += Y_STEP);
964 s_printf(0, y += Y_STEP, "i/o");
965 get_ioread (0, y += Y_STEP);
966 get_iowrite(0, y += Y_STEP);
968 s_printf(0, y += Y_STEP, "net");
969 get_netdown(0, y += Y_STEP);
970 get_netup (0, y += Y_STEP);
971 if (net_stats.have_wlan)
972 get_netsgnl(0, y += Y_STEP);
974 s_printf(0, y += Y_STEP, "batt");
975 get_bcap (0, y += Y_STEP);
976 get_btime (0, y += Y_STEP);
977 get_bvolts (0, y += Y_STEP);
978 get_bwatts (0, y += Y_STEP);
979 get_btemp (0, y += Y_STEP);
981 // print user messages
983 for (i = 0; i < ARRAY_SIZE(user_msgs); i++) {
984 if (user_msgs[i].text[0] == 0)
987 if (user_msgs[i].idle > 6) {
988 // drop old entry, shift others up
989 memmove(&user_msgs[i], &user_msgs[i + 1],
990 (ARRAY_SIZE(user_msgs) - i - 1) * sizeof(user_msgs[0]));
991 user_msgs[ARRAY_SIZE(user_msgs) - 1].text[0] = 0;
997 s_printf(0, y += Y_STEP, user_msgs[i].text);
1004 pthread_cond_timedwait(&g_cond, &mutex, &ts);
1014 // vim:expandtab:sw=2:ts=2