blob: 1e8852002e81af82804ac75b3e07de3bbaad1ad2 [file] [log] [blame]
// 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[-a start_addr_a] [-b start_addr_b]\n"
"Dump the pixels of a selected area from the screen to a file.\n",
prog);
exit(1);
}
unsigned int read_uint(char *s)
{
char *endptr;
unsigned int v = strtoul(s, &endptr, 0);
errno = 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;
int ifd, ofd;
char *src[2], *src_buf[2], *dst, *dst_buf;
int src_offset, dst_offset;
int region_dump = 0;
int num_buffer = 1;
char *filename;
int opt;
int i, j;
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) {
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;
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;
}
ifd = open("/dev/mem", O_RDWR | O_SYNC);
if (ifd == -1) {
perror("can't open /dev/mem\n");
exit(1);
}
ofd = open(filename, O_RDWR | O_CREAT, 0644);
if (ofd == -1) {
perror("can't open dest file\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 | PROT_WRITE,
MAP_SHARED, ifd, fb_start[i]);
if (src[i] == MAP_FAILED) {
perror("cannot mmap src\n");
exit(1);
}
}
ftruncate(ofd, area_size);
dst = mmap(0, area_size, PROT_READ | PROT_WRITE, MAP_SHARED, ofd, 0);
if (dst == MAP_FAILED) {
perror("cannot mmap dst\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) {
src_buf[0] = malloc(screen_size);
src_buf[1] = malloc(screen_size);
memcpy(src_buf[0], src[0], screen_size);
memcpy(src_buf[1], src[1], screen_size);
/* Copy 2 RGB pixels each loop */
for (i = 0; i < screen_size; i += byte_per_pixel) {
for (j = 0; j < byte_per_pixel; j++) {
dst_buf[2 * i + j] = src_buf[0][i + j];
dst_buf[2 * i + byte_per_pixel + j] = src_buf[1][i + j];
}
}
free(src_buf[0]);
free(src_buf[1]);
} else {
memcpy(dst_buf, src[0], area_size);
}
if (region_dump) {
src_offset = area_y * screen_width + area_x;
dst_offset = 0;
for (i = 0; i < area_height; i++) {
memcpy(dst + dst_offset, dst_buf + src_offset, area_width);
src_offset += screen_width;
dst_offset += area_width;
}
}
for (i = 0; i < num_buffer; i++)
munmap(src[i], page_aligned_size);
munmap(dst, area_size);
close(ofd);
close(ifd);
return 0;
}