changeset 10:078dc69945ad

working: libjpeg-turbo support, handle fb and monitor with different size
author Peter Meerwald <pmeerw@pmeerw.net>
date Sun, 15 May 2011 17:48:57 +0200
parents c7af696b6221
children 8fadd7877f6b
files Makefile common.h disp-minimon.c jpg.c minimon.c
diffstat 5 files changed, 423 insertions(+), 80 deletions(-) [+]
line wrap: on
line diff
--- 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
--- 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
--- /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 <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <fcntl.h>
+#include <assert.h>
+#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;
+}
--- 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
--- 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 <stdio.h>
 #include <string.h>
 #include <fcntl.h>
+#include <sys/ioctl.h>
+#include <sys/mman.h>
 #include <assert.h>
 #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 </dev/fbX>\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;
 }

Repositories maintained by Peter Meerwald, pmeerw@pmeerw.net.