| // Copyright (c) 2014 The Chromium OS Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| /* Pixel Dump Utility |
| * |
| * This is a command-line tool running on Chameleon board to dump the |
| * pixels from the Chameleon framebuffer to a given file. |
| */ |
| |
| #include <errno.h> |
| #include <fcntl.h> |
| #include <stdio.h> |
| #include <stdlib.h> |
| #include <string.h> |
| #include <sys/mman.h> |
| #include <unistd.h> |
| |
| static char *prog = NULL; |
| |
| |
| void usage_exit() |
| { |
| fprintf(stderr, |
| "Usage:\t%s filename screen_width screen_height byte_per_pixel \\\n" |
| "\t[area_x area_y area_width area_height]\\\n" |
| "\t[area_skip_pixel area_skip_line]\\\n" |
| "\t[-a start_addr_a] [-b start_addr_b]\n" |
| "Dump the pixels of a selected area from the screen to a file.\n" |
| "Support dumping to stdout with filename set to '-'.\n", |
| prog); |
| exit(1); |
| } |
| |
| |
| unsigned int read_uint(char *s) |
| { |
| char *endptr; |
| unsigned int v; |
| |
| errno = 0; |
| v = strtoul(s, &endptr, 0); |
| |
| if (errno || *endptr != '\0') { |
| fprintf(stderr, "failed to parse argument: '%s'\n", s); |
| usage_exit(); |
| } |
| return v; |
| } |
| |
| |
| int main(int argc, char **argv) |
| { |
| unsigned long fb_start[2] = {0xc0000000, 0}; |
| unsigned long screen_width, screen_height, byte_per_pixel; |
| unsigned long area_x, area_y, area_width = 0, area_height = 0; |
| unsigned long screen_size, page_aligned_size, area_size; |
| unsigned long area_skip_pixel, area_skip_line; |
| int ifd, ofd; |
| char *src[2], *dst, *dst_buf; |
| char *src_ptr, *dst_ptr; |
| int src_offset; |
| int region_dump = 0; |
| int num_buffer = 1; |
| int dump_to_stdout = 0; |
| char *filename; |
| int opt; |
| int i; |
| int dummy __attribute__((unused)); // ignore a compile warning of ftruncate() |
| |
| prog = argv[0]; |
| while ((opt = getopt(argc, argv, "a:b:")) != -1) { |
| switch (opt) { |
| case 'a': |
| fb_start[0] = read_uint(optarg); |
| break; |
| case 'b': |
| fb_start[1] = read_uint(optarg); |
| num_buffer = 2; |
| break; |
| default: |
| usage_exit(); |
| } |
| } |
| |
| if (optind + 4 != argc && optind + 8 != argc && optind + 10 != argc) { |
| usage_exit(); |
| } |
| |
| filename = argv[optind]; |
| byte_per_pixel = read_uint(argv[optind + 3]); |
| /* Use the term "screen" for the src while the term "area" for the dst. */ |
| screen_width = read_uint(argv[optind + 1]) * byte_per_pixel; |
| screen_height = read_uint(argv[optind + 2]); |
| screen_size = screen_width * screen_height; |
| area_size = screen_size * num_buffer; |
| area_skip_pixel = 0; |
| area_skip_line = 0; |
| |
| if (optind + 4 < argc) { |
| region_dump = 1; |
| area_x = read_uint(argv[optind + 4]) * byte_per_pixel; |
| area_y = read_uint(argv[optind + 5]); |
| area_width = read_uint(argv[optind + 6]) * byte_per_pixel; |
| area_height = read_uint(argv[optind + 7]); |
| area_size = area_width * area_height; |
| } |
| |
| if (optind + 8 < argc) { |
| area_skip_pixel = read_uint(argv[optind + 8]); |
| area_skip_line = read_uint(argv[optind + 9]); |
| area_size = 1 + (area_width - 1) / (1 + area_skip_pixel); // ceiling |
| area_size *= 1 + (area_height - 1) / (1 + area_skip_line); // ceiling |
| } |
| |
| if (num_buffer == 2 && byte_per_pixel != 3) { |
| perror("unsupported byte_per_pixel\n"); |
| exit(1); |
| } |
| |
| ifd = open("/dev/mem", O_RDWR | O_SYNC); |
| if (ifd == -1) { |
| perror("can't open /dev/mem\n"); |
| exit(1); |
| } |
| |
| if (strcmp(filename, "-") == 0) { |
| dump_to_stdout = 1; |
| dst = malloc(area_size); |
| } else { |
| ofd = open(filename, O_RDWR | O_CREAT, 0644); |
| if (ofd == -1) { |
| perror("can't open dest file\n"); |
| exit(1); |
| } |
| dummy = ftruncate(ofd, area_size); |
| dst = mmap(0, area_size, PROT_WRITE, MAP_SHARED, ofd, 0); |
| if (dst == MAP_FAILED) { |
| perror("cannot mmap dst\n"); |
| exit(1); |
| } |
| } |
| |
| page_aligned_size = screen_size + (-screen_size % getpagesize()); |
| for (i = 0; i < num_buffer; i++) { |
| src[i] = mmap(0, page_aligned_size, PROT_READ, |
| MAP_SHARED, ifd, fb_start[i]); |
| if (src[i] == MAP_FAILED) { |
| perror("cannot mmap src\n"); |
| exit(1); |
| } |
| } |
| |
| if (region_dump) { |
| /* Store to a buffer for selecting the area later. */ |
| dst_buf = malloc(screen_size * num_buffer); |
| } else { |
| /* Directly dump to the destination. */ |
| dst_buf = dst; |
| } |
| |
| if (num_buffer == 2) { |
| for (dst_ptr = dst_buf, src_ptr = src[0]; |
| dst_ptr < dst_buf + screen_size * 2;) { |
| *dst_ptr++ = *src_ptr++; |
| *dst_ptr++ = *src_ptr++; |
| *dst_ptr++ = *src_ptr++; |
| dst_ptr += 3; |
| } |
| for (dst_ptr = dst_buf + 3, src_ptr = src[1]; |
| dst_ptr < dst_buf + screen_size * 2;) { |
| *dst_ptr++ = *src_ptr++; |
| *dst_ptr++ = *src_ptr++; |
| *dst_ptr++ = *src_ptr++; |
| dst_ptr += 3; |
| } |
| } else { |
| memcpy(dst_buf, src[0], screen_size); |
| } |
| |
| if (region_dump) { |
| src_offset = area_y * screen_width + area_x; |
| for (dst_ptr = dst; dst_ptr < dst + area_size;) { |
| for (src_ptr = dst_buf + src_offset; |
| src_ptr < dst_buf + src_offset + area_width;) { |
| *dst_ptr++ = *src_ptr++; |
| *dst_ptr++ = *src_ptr++; |
| *dst_ptr++ = *src_ptr++; |
| src_ptr += area_skip_pixel * byte_per_pixel; |
| } |
| src_offset += screen_width * (1 + area_skip_line); |
| } |
| } |
| for (i = 0; i < num_buffer; i++) |
| munmap(src[i], page_aligned_size); |
| |
| if (dump_to_stdout) { |
| fwrite(dst, 1, area_size, stdout); |
| fflush(stdout); |
| } else { |
| munmap(dst, area_size); |
| close(ofd); |
| } |
| |
| close(ifd); |
| return 0; |
| } |