# HG changeset patch # User Peter Meerwald # Date 1305474537 -7200 # Node ID 078dc69945ad839e2aa6827bc2d69f663ce0143c # Parent c7af696b62216c559d3194e0c9d056fe745ea1e7 working: libjpeg-turbo support, handle fb and monitor with different size diff -r c7af696b6221 -r 078dc69945ad Makefile --- a/Makefile Sat May 14 23:08:11 2011 +0200 +++ b/Makefile Sun May 15 17:48:57 2011 +0200 @@ -1,13 +1,18 @@ -all: minimon fbt +# CC=gcc -O2 -march=native -fomit-frame-pointer + +all: minimon minimon-turbo minimon: minimon.c jpg.c $(CC) $(CFLAGS) -I. -Wall -g -o $@ minimon.c jpg.c fb.c -lusb -ljpeg +minimon-turbo: minimon.c jpg.c + $(CC) $(CFLAGS) -DHAVE_LIBJPEG_TURBO -I. -I../libjpeg-turbo -Wall -g -o $@ minimon.c jpg.c fb.c -lusb ../libjpeg-turbo/.libs/libturbojpeg.a + fbt: fbt.c jpg.c $(CC) $(CFLAGS) -I. -Wall -g -o $@ fbt.c jpg.c -ljpeg clean: - rm -f minimon fbt + rm -f minimon minimon-turbo fbt install: install -d $(DESTDIR)/usr/bin diff -r c7af696b6221 -r 078dc69945ad common.h --- a/common.h Sat May 14 23:08:11 2011 +0200 +++ b/common.h Sun May 15 17:48:57 2011 +0200 @@ -1,11 +1,15 @@ #ifndef COMMON_H #define COMMON_H +#ifndef MIN +#define MIN(x, y) ((x < y) ? (x) : (y)) +#endif + typedef struct { unsigned long size; unsigned char *ptr; } jpg_buf_t; -jpg_buf_t build_jpg_from_fb(unsigned char *fb_mem, int width, int height, int bits_per_pixel); +jpg_buf_t build_jpg_from_fb(unsigned char *fb_mem, int fb_width, int fb_height, int bits_per_pixel, int mon_width, int mon_height); #endif diff -r c7af696b6221 -r 078dc69945ad disp-minimon.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/disp-minimon.c Sun May 15 17:48:57 2011 +0200 @@ -0,0 +1,212 @@ +#include +#include +#include +#include +#include +#include "usb.h" + +static const char *progname = "minimon"; + +static int need_switch = 0; +static int have_idx = -1; + +typedef struct { + int mass_id; + int custom_id; + const char *name; + int width; + int height; +} id_info_t; + +// there are many more, +static id_info_t ids[] = { + {0x2027, 0x2028, "SPF-107H", 1024, 600}, + {0xffff, 0xffff, "SPF-75H", 800, 480}, + {0xffff, 0xffff, "SPF-83H", 800, 600}, + {0xffff, 0xffff, "SPF-85H", 800, 600}, // 85P?? + {0x2033, 0x2034, "SPF-87H", 800, 480}, + {0x2035, 0x2036, "SPF-107H", 1024, 600}, + {0, 0, } // end-of-list +}; + +static int in_list(int id, id_info_t *list) { + if (!list) + return 0; + + int idx = 0; + while (list->mass_id || list->custom_id) { + if (id == list->mass_id) { + // still in mass-storage mode, need to switch + need_switch = 1; + return idx; + } + else if (id == list->custom_id) { + need_switch = 0; + return idx; + } + idx++; + list++; + } + + return -1; +} + +static struct usb_device *find_dev() { + struct usb_bus *bus; + struct usb_device *dev; + + usb_init(); + usb_find_busses(); + usb_find_devices(); + + for (bus = usb_busses; bus; bus = bus->next) { + for (dev = bus->devices; dev; dev = dev->next) { + if (dev->descriptor.idVendor == 0x04e8) { + // found a Samsung device, good + int idx = -1; + if ((idx = in_list(dev->descriptor.idProduct, ids)) >= 0) { + have_idx = idx; + return dev; + } + } + } + } + + return NULL; +} + +static usb_dev_handle *dev_open(struct usb_device *dev) { + int res = -1; + usb_dev_handle *udev; + int numeps = 0; + + udev = usb_open(dev); + if (!udev) { + fprintf(stderr, "%s: failed to open device, exit.\n", progname); + exit(EXIT_FAILURE); + } + +// setuid(getuid()); + + res = usb_set_configuration(udev, 1); + + usb_claim_interface(udev, 0); + numeps = dev->config[0].interface[0].altsetting[0].bNumEndpoints; + if (numeps == 0) { + fprintf(stderr, "%s: no endpoints, exit.\n", progname); + exit(EXIT_FAILURE); + } + + { + int eplist[] = { 0x2, 0x81, 0x83 }; + int eplength = sizeof(eplist)/sizeof(eplist[0]); + int *endpoint = eplist; + int i; + for (i = 0; i < eplength; i++) { + res = usb_resetep(udev, *endpoint); + res = usb_clear_halt(udev, *endpoint); + endpoint++; + } + } + + return udev; +} + +static void send_jpeg(FILE *f, usb_dev_handle *udev) { + fseek(f, 0, SEEK_END); + int sz = ftell(f); + fseek(f, 0, SEEK_SET); + + #define URBBUF_MAX 0x20000 + char buf[URBBUF_MAX]; + + #define HDR_LEN 12 + char hdr[HDR_LEN] = {0xa5, 0x5a, 0x18, 0x04, 0xff, 0xff, 0xff, 0xff, 0x48, 0x00, 0x00, 0x00}; + *(int *)(hdr+4) = sz; + + memcpy(buf, hdr, HDR_LEN); + int off = HDR_LEN; + + while(!feof(f)) { + int nr = fread(buf+off, 1, URBBUF_MAX - off, f); + if (nr < 0) break; + // pad + memset(buf + off + nr, 0, URBBUF_MAX - off - nr); + + // write it out chunk by chunk + int timeout = 1000; + int endpoint = 0x2; + int res = usb_bulk_write(udev, endpoint, buf, URBBUF_MAX, timeout); + + assert(res >= 0); + off = 0; // no header on subsequent chunks + } +} + +int main(int argc, char *argv[]) { + if (argc != 2) { + fprintf(stderr, "Usage: %s <.jpg file>\n", progname); + return EXIT_FAILURE; + } + + struct usb_device *dev = find_dev(index); + if (!dev) { + fprintf(stderr, "%s: no photo frame device found, exit.\n", progname); + exit(EXIT_FAILURE); + } + + if (need_switch) { + fprintf(stderr, "%s: found %s, trying to switch to custom product mode...\n", + ids[have_idx].name, progname); + + usb_dev_handle *udev; + + udev = usb_open(dev); + if (!udev) { + fprintf(stderr, "%s: failed to open device, exit.\n", progname); + exit(EXIT_FAILURE); + } + + char buf[254]; + memset(buf, 0, 254); + + int res = usb_control_msg(udev, USB_TYPE_STANDARD | USB_ENDPOINT_IN, USB_REQ_GET_DESCRIPTOR, + 0xfe, 0xfe, buf, 0xfe, 1000); + fprintf(stderr, "%s: usb_control_msg() = %d\n", progname, res); + + usb_close(udev); + usleep(500000); + } + + dev = find_dev(index); + if (!dev || need_switch) { + fprintf(stderr, "%s: no photo frame device found, exit.\n", progname); + exit(EXIT_FAILURE); + } + + fprintf(stderr, "%s: found %s (%d x %d)\n", + progname, ids[have_idx].name, ids[have_idx].width, ids[have_idx].height); + + usb_dev_handle *udev = dev_open(dev); + + FILE *f = fopen(argv[1], "rb"); + if (f == NULL) { + fprintf(stderr, "%s: failed to open file '%s', exit.\n", progname, argv[1]); + exit(EXIT_FAILURE); + } + send_jpeg(f, udev); + fclose(f); + + while (1) { + char buf[2]; + int res = usb_control_msg(udev, USB_TYPE_VENDOR | USB_ENDPOINT_IN, USB_REQ_GET_DESCRIPTOR, + 0x0, 0x0, buf, 0x2, 1000); + fprintf(stderr, "%s: usb_control_msg() = %d\n", progname, res); + + usleep(500000); + } + + usb_close(udev); + + return EXIT_SUCCESS; +} diff -r c7af696b6221 -r 078dc69945ad jpg.c --- a/jpg.c Sat May 14 23:08:11 2011 +0200 +++ b/jpg.c Sun May 15 17:48:57 2011 +0200 @@ -92,17 +92,8 @@ dest->pub.free_in_buffer = dest->bufsize = *outsize; } -static void convert_rgba_to_rgb(unsigned char *buf, unsigned char *fb_mem, int width) { - int i; - for (i = 0; i < width; i++) { - *buf++ = *fb_mem++; - *buf++ = *fb_mem++; - *buf++ = *fb_mem++; - fb_mem++; - } -} - -jpg_buf_t build_jpg_from_fb(unsigned char *fb_mem, int width, int height, int bits_per_pixel) { +#ifdef HAVE_LIBJPEG_TURBO +jpg_buf_t build_jpg_from_fb(unsigned char *fb_mem, int fb_width, int fb_height, int bits_per_pixel, int mon_width, int mon_height) { int scanline; struct jpeg_compress_struct cinfo; struct jpeg_error_mgr jerr; @@ -116,21 +107,34 @@ my_jpeg_mem_dest(&cinfo, &jpg_buf.ptr, &jpg_buf.size); - cinfo.image_width = width; - cinfo.image_height = height; - cinfo.input_components = 3; - cinfo.in_color_space = JCS_RGB; + cinfo.image_width = mon_width; + cinfo.image_height = mon_height; + cinfo.input_components = 4; + cinfo.in_color_space = JCS_EXT_BGRX; jpeg_set_defaults(&cinfo); - + cinfo.dct_method = JDCT_FASTEST; + jpeg_set_quality(&cinfo, 70, TRUE); + jpeg_start_compress(&cinfo, TRUE); - JSAMPLE *buf = (JSAMPLE *) malloc(width * 3); + unsigned char buf[mon_width * 4]; JSAMPROW row_ptr[1] = {buf}; - for (scanline = 0; scanline < height; scanline++) { - convert_rgba_to_rgb(buf, &fb_mem[scanline * width * 4], width); + memset(buf, 0, mon_width*4); + if (fb_width >= mon_width) + for (scanline = 0; scanline < MIN(fb_height, mon_height); scanline++) { + row_ptr[0] = &fb_mem[scanline * fb_width * 4]; + jpeg_write_scanlines(&cinfo, row_ptr, 1); + } + else { + for (scanline = 0; scanline < MIN(fb_height, mon_height); scanline++) { + memcpy(buf, &fb_mem[scanline * fb_width * 4], fb_width * 4); + jpeg_write_scanlines(&cinfo, row_ptr, 1); + } + } + memset(buf, 0, mon_width*4); + for (scanline = fb_height; scanline < mon_height; scanline++) { jpeg_write_scanlines(&cinfo, row_ptr, 1); } - free(buf); jpeg_finish_compress(&cinfo); @@ -138,3 +142,59 @@ return jpg_buf; } +#else +static void convert_rgba_to_rgb(unsigned char *buf, unsigned char *fb_mem, int width) { + int i; + for (i = 0; i < width; i++) { +// BGRX + unsigned int x = *(unsigned int *)fb_mem; + *buf++ = (x >> 16) & 0xff; + *buf++ = (x >> 8) & 0xff; + *buf++ = (x) & 0xff; + fb_mem += 4; + } +} + +jpg_buf_t build_jpg_from_fb(unsigned char *fb_mem, int fb_width, int fb_height, int bits_per_pixel, int mon_width, int mon_height) { + int scanline; + struct jpeg_compress_struct cinfo; + struct jpeg_error_mgr jerr; + + cinfo.err = jpeg_std_error(&jerr); + jpeg_create_compress(&cinfo); + + jpg_buf_t jpg_buf; + jpg_buf.size = 1 << 20; + jpg_buf.ptr = NULL; + + my_jpeg_mem_dest(&cinfo, &jpg_buf.ptr, &jpg_buf.size); + + cinfo.image_width = mon_width; + cinfo.image_height = mon_height; + cinfo.input_components = 3; + cinfo.in_color_space = JCS_RGB; + jpeg_set_defaults(&cinfo); + cinfo.dct_method = JDCT_FASTEST; + jpeg_set_quality(&cinfo, 70, TRUE); + + jpeg_start_compress(&cinfo, TRUE); + + JSAMPLE buf[mon_width * 3]; + JSAMPROW row_ptr[1] = {buf}; + memset(buf, 0, mon_width*3); + for (scanline = 0; scanline < MIN(fb_height, mon_height); scanline++) { + convert_rgba_to_rgb(buf, &fb_mem[scanline * fb_width * 4], mon_width); + jpeg_write_scanlines(&cinfo, row_ptr, 1); + } + memset(buf, 0, mon_width*3); + for (scanline = fb_height; scanline < mon_height; scanline++) { + jpeg_write_scanlines(&cinfo, row_ptr, 1); + } + + jpeg_finish_compress(&cinfo); + + jpeg_destroy_compress(&cinfo); + + return jpg_buf; +} +#endif diff -r c7af696b6221 -r 078dc69945ad minimon.c --- a/minimon.c Sat May 14 23:08:11 2011 +0200 +++ b/minimon.c Sun May 15 17:48:57 2011 +0200 @@ -2,9 +2,15 @@ #include #include #include +#include +#include #include #include "usb.h" +#include "linux/fb.h" + +#include "common.h" + static const char *progname = "minimon"; static int need_switch = 0; @@ -112,24 +118,21 @@ return udev; } -static void send_jpeg(FILE *f, usb_dev_handle *udev) { - fseek(f, 0, SEEK_END); - int sz = ftell(f); - fseek(f, 0, SEEK_SET); - +static int send_jpg(jpg_buf_t *jpg_buf, usb_dev_handle *udev) { #define URBBUF_MAX 0x20000 char buf[URBBUF_MAX]; #define HDR_LEN 12 char hdr[HDR_LEN] = {0xa5, 0x5a, 0x18, 0x04, 0xff, 0xff, 0xff, 0xff, 0x48, 0x00, 0x00, 0x00}; - *(int *)(hdr+4) = sz; + *(int *)(hdr+4) = jpg_buf->size; memcpy(buf, hdr, HDR_LEN); int off = HDR_LEN; - - while(!feof(f)) { - int nr = fread(buf+off, 1, URBBUF_MAX - off, f); - if (nr < 0) break; + int jpg_off = 0; + int jpg_left = jpg_buf->size; + while (jpg_left > 0) { + int nr = MIN(URBBUF_MAX - off, jpg_left); + memcpy(buf+off, &jpg_buf->ptr[jpg_off], nr); // pad memset(buf + off + nr, 0, URBBUF_MAX - off - nr); @@ -138,75 +141,134 @@ int endpoint = 0x2; int res = usb_bulk_write(udev, endpoint, buf, URBBUF_MAX, timeout); - assert(res >= 0); + if (res < 0) + return 0; off = 0; // no header on subsequent chunks + jpg_off += nr; + jpg_left -= nr; } + + return 1; } int main(int argc, char *argv[]) { if (argc != 2) { - fprintf(stderr, "Usage: %s <.jpg file>\n", progname); + fprintf(stderr, "Usage: %s \n", progname); return EXIT_FAILURE; } - struct usb_device *dev = find_dev(index); - if (!dev) { - fprintf(stderr, "%s: no photo frame device found, exit.\n", progname); + int fd = open(argv[1], O_RDONLY); + if (fd < 0) { + perror("minimon framebuffer"); + exit(EXIT_FAILURE); + } + + struct fb_fix_screeninfo sif; + if (ioctl(fd, FBIOGET_FSCREENINFO, &sif) < 0) { + perror("minimon framebuffer info"); + exit(EXIT_FAILURE); + } + + printf("id %s\n", sif.id); + printf("type %d, aux %d\n", sif.type, sif.type_aux); + printf("visual %d\n", sif.visual); + printf("accel %d\n", sif.accel); + printf("line length %d\n", sif.line_length); + printf("mem %d\n", sif.smem_len); + + struct fb_var_screeninfo siv; + if (ioctl(fd, FBIOGET_VSCREENINFO, &siv) < 0) { + perror("minimon framebuffer info"); + exit(EXIT_FAILURE); + } + + printf("res x %d y %d\n", siv.xres, siv.yres); + printf("bpp %d\n", siv.bits_per_pixel); + + size_t fb_mem_size = siv.xres * siv.yres * siv.bits_per_pixel / 8; + void *fb_mem = mmap(NULL, fb_mem_size, PROT_READ, MAP_SHARED, fd, 0); + if (fb_mem == MAP_FAILED) { + perror("minimon framebuffer mapping"); exit(EXIT_FAILURE); } - if (need_switch) { - fprintf(stderr, "%s: found %s, trying to switch to custom product mode...\n", + while (1) { + struct usb_device *dev = find_dev(index); + if (!dev) { + fprintf(stderr, "%s: no photo frame device found, suspending...\n", progname); + sleep(1); + continue; + } + + if (need_switch) { + fprintf(stderr, "%s: found %s, trying to switch to custom product mode...\n", ids[have_idx].name, progname); - usb_dev_handle *udev; + usb_dev_handle *udev; + udev = usb_open(dev); + if (!udev) { + fprintf(stderr, "%s: failed to open device, exit.\n", progname); + exit(EXIT_FAILURE); + } - udev = usb_open(dev); - if (!udev) { - fprintf(stderr, "%s: failed to open device, exit.\n", progname); - exit(EXIT_FAILURE); + char buf[254]; + memset(buf, 0, 254); + int res = usb_control_msg(udev, USB_TYPE_STANDARD | USB_ENDPOINT_IN, + 0x06, 0xfe, 0xfe, buf, 0xfe, 1000); + fprintf(stderr, "%s: usb_control_msg() = %d\n", progname, res); + usb_close(udev); + sleep(1); + } + + dev = find_dev(index); + if (!dev || need_switch) { + fprintf(stderr, "%s: no photo frame device found, suspending...\n", progname); + sleep(1); + continue; } - char buf[254]; - memset(buf, 0, 254); + int mon_width = ids[have_idx].width; + int mon_height = ids[have_idx].height; + fprintf(stderr, "%s: found %s (%d x %d)\n", + progname, ids[have_idx].name, mon_width, mon_height); + + usb_dev_handle *udev = dev_open(dev); - int res = usb_control_msg(udev, USB_TYPE_STANDARD | USB_ENDPOINT_IN, USB_REQ_GET_DESCRIPTOR, - 0xfe, 0xfe, buf, 0xfe, 1000); - fprintf(stderr, "%s: usb_control_msg() = %d\n", progname, res); + while (1) { + int transfer = 1; + + // poll status to avoid going back to photoframe mode + char buf[2]; + int res = usb_control_msg(udev, USB_TYPE_VENDOR | USB_ENDPOINT_IN, + 0x06, 0x0, 0x0, buf, 0x2, 1000); + if (res != 2) { + break; + } + else if (buf[0] != 0) { + transfer = 0; + } + + if (transfer) { + fprintf(stderr, "."); + jpg_buf_t jpg_buf = build_jpg_from_fb((unsigned char *)fb_mem, siv.xres, siv.yres, siv.bits_per_pixel, mon_width, mon_height); + + if (!send_jpg(&jpg_buf, udev)) { + free(jpg_buf.ptr); + break; + } + free(jpg_buf.ptr); + } + else + fprintf(stderr, "o"); + + usleep(500000); + } usb_close(udev); - usleep(500000); } - dev = find_dev(index); - if (!dev || need_switch) { - fprintf(stderr, "%s: no photo frame device found, exit.\n", progname); - exit(EXIT_FAILURE); - } - - fprintf(stderr, "%s: found %s (%d x %d)\n", - progname, ids[have_idx].name, ids[have_idx].width, ids[have_idx].height); - - usb_dev_handle *udev = dev_open(dev); - - FILE *f = fopen(argv[1], "rb"); - if (f == NULL) { - fprintf(stderr, "%s: failed to open file '%s', exit.\n", progname, argv[1]); - exit(EXIT_FAILURE); - } - send_jpeg(f, udev); - fclose(f); - - while (1) { - char buf[2]; - int res = usb_control_msg(udev, USB_TYPE_VENDOR | USB_ENDPOINT_IN, USB_REQ_GET_DESCRIPTOR, - 0x0, 0x0, buf, 0x2, 1000); - fprintf(stderr, "%s: usb_control_msg() = %d\n", progname, res); - - usleep(500000); - } - - usb_close(udev); + munmap(fb_mem, fb_mem_size); + close(fd); return EXIT_SUCCESS; }