kopia lustrzana https://gitlab.com/sane-project/backends
				
				
				
			
		
			
				
	
	
		
			1096 wiersze
		
	
	
		
			33 KiB
		
	
	
	
		
			C
		
	
	
			
		
		
	
	
			1096 wiersze
		
	
	
		
			33 KiB
		
	
	
	
		
			C
		
	
	
| /* sane - Scanner Access Now Easy.
 | |
|    Copyright (C) 1999 Juergen G. Schimmer
 | |
| 
 | |
|    This file is part of the SANE package.
 | |
| 
 | |
|    This program is free software; you can redistribute it and/or
 | |
|    modify it under the terms of the GNU General Public License as
 | |
|    published by the Free Software Foundation; either version 2 of the
 | |
|    License, or (at your option) any later version.
 | |
| 
 | |
|    This program is distributed in the hope that it will be useful, but
 | |
|    WITHOUT ANY WARRANTY; without even the implied warranty of
 | |
|    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 | |
|    General Public License for more details.
 | |
| 
 | |
|    You should have received a copy of the GNU General Public License
 | |
|    along with this program; if not, write to the Free Software
 | |
|    Foundation, Inc., 59 Temple Place - Suite 330, Boston,
 | |
|    MA 02111-1307, USA.
 | |
| 
 | |
|    As a special exception, the authors of SANE give permission for
 | |
|    additional uses of the libraries contained in this release of SANE.
 | |
| 
 | |
|    The exception is that, if you link a SANE library with other files
 | |
|    to produce an executable, this does not by itself cause the
 | |
|    resulting executable to be covered by the GNU General Public
 | |
|    License.  Your use of that executable is in no way restricted on
 | |
|    account of linking the SANE library code into it.
 | |
| 
 | |
|    This exception does not, however, invalidate any other reasons why
 | |
|    the executable file might be covered by the GNU General Public
 | |
|    License.
 | |
| 
 | |
|    If you submit changes to SANE to the maintainers to be included in
 | |
|    a subsequent release, you agree by submitting the changes that
 | |
|    those changes may be distributed with this exception intact.
 | |
| 
 | |
|    If you write modifications of your own for SANE, it is your choice
 | |
|    whether to permit this exception to apply to your modifications.
 | |
|    If you do not wish that, delete this exception notice.
 | |
| 
 | |
|    This file implements a SANE backend for v4l-Devices.  At
 | |
|    present, only the Quickcam and bttv is supported though the driver
 | |
|    should be able to easily accommodate other v4l-Devices.
 | |
| 
 | |
|    THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
 | |
|    EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
 | |
|    MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
 | |
|    NONINFRINGEMENT.  IN NO EVENT SHALL SCOTT LAIRD BE LIABLE FOR ANY
 | |
|    CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
 | |
|    CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
 | |
|    WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.  */
 | |
| 
 | |
| #ifdef _AIX
 | |
| # include "lalloca.h"   /* MUST come first for AIX! */
 | |
| #endif
 | |
| 
 | |
| #include "sane/config.h"
 | |
| #include "lalloca.h"
 | |
| 
 | |
| #include <assert.h>
 | |
| #include <ctype.h>
 | |
| #include <errno.h>
 | |
| #include <fcntl.h>
 | |
| #include <limits.h>
 | |
| #include <math.h>
 | |
| #include <setjmp.h>
 | |
| #include <signal.h>
 | |
| #include <stdio.h>
 | |
| #include <stdlib.h>
 | |
| #include <string.h>
 | |
| #include <sys/types.h>
 | |
| #include <sys/wait.h>
 | |
| #include <unistd.h>
 | |
| #include <sys/mman.h>
 | |
| 
 | |
| #include <unistd.h>
 | |
| #include <sys/time.h>
 | |
| #include <sys/stat.h>
 | |
| 
 | |
| 
 | |
| #include "sane/sane.h"
 | |
| #include "sane/sanei.h"
 | |
| #include "sane/saneopts.h"
 | |
| 
 | |
| #include "v4l-grab.h"
 | |
| #if what_is_this
 | |
| #  include "colorspace.h"
 | |
| #endif
 | |
| #include <sys/ioctl.h>
 | |
| #include <asm/types.h>          /* XXX glibc */
 | |
| #include <linux/videodev.h>
 | |
| 
 | |
| #define SYNC_TIMEOUT 1
 | |
| 
 | |
| #define TRUE  1
 | |
| #define FALSE 0
 | |
| 
 | |
| #include "v4l.h"
 | |
| 
 | |
| #define BACKEND_NAME v4l
 | |
| #include "sane/sanei_backend.h"
 | |
| 
 | |
| #ifndef PATH_MAX
 | |
| # define PATH_MAX       1024
 | |
| #endif
 | |
| 
 | |
| #include "sane/sanei_config.h"
 | |
| #define V4L_CONFIG_FILE "v4l.conf"
 | |
| 
 | |
| static const SANE_Device ** devlist = NULL;
 | |
| static int num_devices;
 | |
| static V4L_Device * first_dev;
 | |
| static V4L_Scanner * first_handle;
 | |
| static int v4ldev;
 | |
| static char *buffer;
 | |
| static int buffercount;
 | |
| 
 | |
| 
 | |
| static const SANE_String_Const resolution_list[] =
 | |
|   {
 | |
|     "Low",      /* million-mode */
 | |
|     "High",     /* billion-mode */
 | |
|     0
 | |
|   };
 | |
| 
 | |
| 
 | |
| static const SANE_Int color_depth_list[] =
 | |
|   {
 | |
|     3 ,                 /* # of elements */
 | |
|     6, 8, 24            /* "thousand" mode not implemented yet */
 | |
|   };
 | |
| 
 | |
| static const SANE_Int xfer_scale_list[] =
 | |
|   {
 | |
|     4,                          /* # of elements */
 | |
|     1, 2, 4 , 8
 | |
|   };
 | |
| 
 | |
| static const SANE_Range u8_range =
 | |
|   {
 | |
|     /* min, max, quantization */
 | |
|     0, 255, 0
 | |
|   };
 | |
| 
 | |
| static const SANE_Range brightness_range =
 | |
|   {
 | |
|     /* min, max, quantization */
 | |
|     0, 255, 0                   /* 255 is bulb mode! */
 | |
|   };
 | |
| 
 | |
| SANE_Range x_range = {0,338,2};
 | |
| 
 | |
| SANE_Range odd_x_range = {1,339,2};
 | |
| 
 | |
| SANE_Range y_range = {0,249,1};
 | |
| 
 | |
| SANE_Range odd_y_range ={1,250,1};
 | |
| 
 | |
| 
 | |
| static SANE_Parameters parms =
 | |
|   {
 | |
|      SANE_FRAME_RGB,
 | |
|      1,                          /* 1 = Last Frame , 0 = More Frames to come */
 | |
|      0,                          /* Number of bytes returned per scan line: */
 | |
|      0,                          /* Number of pixels per scan line.  */
 | |
|      0,                          /* Number of lines for the current scan.  */
 | |
|      8,                          /* Number of bits per sample. */
 | |
|    };
 | |
| 
 | |
| 
 | |
| static struct video_capability  capability;
 | |
| static struct video_picture     pict;
 | |
| static struct video_window      window;
 | |
| static struct video_mbuf        ov_mbuf;
 | |
| static struct video_mmap        gb;
 | |
| 
 | |
| static SANE_Status
 | |
| attach (const char *devname, V4L_Device **devp)
 | |
| {
 | |
|   V4L_Device * q;
 | |
| 
 | |
|   if (!devp)
 | |
|     return SANE_STATUS_INVAL;
 | |
| 
 | |
|   errno = 0;
 | |
| 
 | |
|   q = malloc (sizeof (*q));
 | |
|   if (!q)
 | |
|     return SANE_STATUS_NO_MEM;
 | |
| 
 | |
|   memset (q, 0, sizeof (*q));
 | |
|   q->lock_fd = -1;
 | |
|   q->version = V4L_COLOR;
 | |
| 
 | |
|   q->sane.name   = strdup (devname);
 | |
|   q->sane.vendor = "V4L";
 | |
|   q->sane.model  = capability.name;
 | |
|   q->sane.type   = "virtual device";
 | |
| 
 | |
|   ++num_devices;
 | |
|   q->next = first_dev;
 | |
|   first_dev = q;
 | |
| 
 | |
| 
 | |
|   *devp = q;
 | |
|   return SANE_STATUS_GOOD;
 | |
| }
 | |
| 
 | |
| 
 | |
| static SANE_Status
 | |
| init_options (V4L_Scanner *s)
 | |
| {
 | |
|   int i;
 | |
| 
 | |
|   memset (s->opt, 0, sizeof (s->opt));
 | |
|   memset (s->val, 0, sizeof (s->val));
 | |
| 
 | |
|   x_range.min   =       0;
 | |
|   x_range.max   =       capability.maxwidth - capability.minwidth;
 | |
|   x_range.quant =       1;
 | |
| 
 | |
|   y_range.min   =       0;
 | |
|   y_range.max   =       capability.maxheight - capability.minheight;
 | |
|   y_range.quant =       1;
 | |
| 
 | |
|   odd_x_range.min =     capability.minwidth;
 | |
|   odd_x_range.max =     capability.maxwidth;
 | |
|   if (odd_x_range.max > 767)
 | |
|     {
 | |
|         odd_x_range.max = 767;
 | |
|         x_range.max     = 767 - capability.minwidth;
 | |
|     };
 | |
|   odd_x_range.quant =   1;
 | |
| 
 | |
|   odd_y_range.min =     capability.minheight;
 | |
|   odd_y_range.max =     capability.maxheight;
 | |
|   if (odd_y_range.max > 511)
 | |
|     {
 | |
|         odd_y_range.max = 511;
 | |
|         y_range.max     = 511 - capability.minheight;
 | |
|     };
 | |
|   odd_y_range.quant =   1;
 | |
| 
 | |
|   parms.depth   =       pict.depth;
 | |
|   if (pict.depth < 8)
 | |
|      parms.depth        =       8;
 | |
|   parms.depth   =       8;
 | |
|   parms.lines           = window.height;
 | |
|   parms.pixels_per_line = window.width;
 | |
|   switch(pict.palette)
 | |
|      {
 | |
|         case VIDEO_PALETTE_GREY:        /* Linear greyscale */
 | |
|            {
 | |
|                 parms.format    =       SANE_FRAME_GRAY;
 | |
|                 parms.bytes_per_line  = window.width;
 | |
|                 break;
 | |
|            }
 | |
|         case VIDEO_PALETTE_HI240:       /* High 240 cube (BT848) */
 | |
|            {
 | |
|                 parms.format    =       SANE_FRAME_RGB;
 | |
|                 parms.bytes_per_line  = window.width * 3;
 | |
|                 break;
 | |
|            }
 | |
|         case VIDEO_PALETTE_RGB565:      /* 565 16 bit RGB */
 | |
|            {
 | |
|                 parms.format    =       SANE_FRAME_RGB;
 | |
|                 parms.bytes_per_line  = window.width * 3;
 | |
|                 break;
 | |
|            }
 | |
|         case VIDEO_PALETTE_RGB24:       /* 24bit RGB */
 | |
|            {
 | |
|                 parms.format    =       SANE_FRAME_RGB;
 | |
|                 parms.bytes_per_line  = window.width * 3;
 | |
|                 break;
 | |
|            }
 | |
|         case VIDEO_PALETTE_RGB32:       /* 32bit RGB */
 | |
|            {
 | |
|                 parms.format    =       SANE_FRAME_RGB;
 | |
|                 parms.bytes_per_line  = window.width * 3;
 | |
|                 break;
 | |
|            }
 | |
|         case VIDEO_PALETTE_RGB555:      /* 555 15bit RGB */
 | |
|            {
 | |
|                 parms.format    =       SANE_FRAME_RGB;
 | |
|                 parms.bytes_per_line  = window.width * 3;
 | |
|                 break;
 | |
|            }
 | |
|         case VIDEO_PALETTE_YUV422:      /* YUV422 capture */
 | |
|            {
 | |
|                 parms.format    =       SANE_FRAME_RGB;
 | |
|                 parms.bytes_per_line  = window.width * 3;
 | |
|                 break;
 | |
|            }
 | |
|         case VIDEO_PALETTE_YUYV:
 | |
|            {
 | |
|                 parms.format    =       SANE_FRAME_RGB;
 | |
|                 parms.bytes_per_line  = window.width * 3;
 | |
|                 break;
 | |
|            }
 | |
|         case VIDEO_PALETTE_UYVY:        /*The great thing about standards is..*/
 | |
|            {
 | |
|                 parms.format    =       SANE_FRAME_RGB;
 | |
|                 parms.bytes_per_line  = window.width * 3;
 | |
|                 break;
 | |
|            }
 | |
|         case VIDEO_PALETTE_YUV420:
 | |
|            {
 | |
|                 parms.format    =       SANE_FRAME_RGB;
 | |
|                 parms.bytes_per_line  = window.width * 3;
 | |
|                 break;
 | |
|            }
 | |
|         case VIDEO_PALETTE_YUV411:      /* YUV411 capture */
 | |
|            {
 | |
|                 parms.format    =       SANE_FRAME_RGB;
 | |
|                 parms.bytes_per_line  = window.width * 3;
 | |
|                 break;
 | |
|            }
 | |
|         case VIDEO_PALETTE_RAW:         /* RAW capture (BT848) */
 | |
|            {
 | |
|                 parms.format    =       SANE_FRAME_RGB;
 | |
|                 parms.bytes_per_line  = window.width * 3;
 | |
|                 break;
 | |
|            }
 | |
|         case VIDEO_PALETTE_YUV422P:     /* YUV 4:2:2 Planar */
 | |
|            {
 | |
|                 parms.format    =       SANE_FRAME_RGB;
 | |
|                 parms.bytes_per_line  = window.width * 3;
 | |
|                 break;
 | |
|            }
 | |
|         case VIDEO_PALETTE_YUV411P:     /* YUV 4:1:1 Planar */
 | |
|            {
 | |
|                 parms.format    =       SANE_FRAME_RGB;
 | |
|                 parms.bytes_per_line  = window.width * 3;
 | |
|                 break;
 | |
|            }
 | |
|         case VIDEO_PALETTE_YUV420P:     /* YUV 4:2:0 Planar */
 | |
|            {
 | |
|                 parms.format    =       SANE_FRAME_RGB;
 | |
|                 parms.bytes_per_line  = window.width * 3;
 | |
|                 break;
 | |
|            }
 | |
|         case VIDEO_PALETTE_YUV410P:     /* YUV 4:1:0 Planar */
 | |
|            {
 | |
|                 parms.format    =       SANE_FRAME_RGB;
 | |
|                 parms.bytes_per_line  = window.width * 3;
 | |
|                 break;
 | |
|            }
 | |
|         default:
 | |
|            {
 | |
|                 parms.format    =       SANE_FRAME_GRAY;
 | |
|                 parms.bytes_per_line  = window.width;
 | |
|                 break;
 | |
|            }
 | |
|      }
 | |
| 
 | |
|   for (i = 0; i < NUM_OPTIONS; ++i)
 | |
|     {
 | |
|       s->opt[i].size = sizeof (SANE_Word);
 | |
|       s->opt[i].cap = (SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT
 | |
|                        | SANE_CAP_ALWAYS_SETTABLE);
 | |
|     }
 | |
| 
 | |
|   s->opt[OPT_NUM_OPTS].title = SANE_TITLE_NUM_OPTIONS;
 | |
|   s->opt[OPT_NUM_OPTS].desc = SANE_DESC_NUM_OPTIONS;
 | |
|   s->opt[OPT_NUM_OPTS].type = SANE_TYPE_INT;
 | |
|   s->opt[OPT_NUM_OPTS].cap = SANE_CAP_SOFT_DETECT;
 | |
|   s->val[OPT_NUM_OPTS].w = NUM_OPTIONS;
 | |
| 
 | |
|   /* "Mode" group: */
 | |
| 
 | |
|   s->opt[OPT_MODE_GROUP].title = "Scan Mode";
 | |
|   s->opt[OPT_MODE_GROUP].desc = "";
 | |
|   s->opt[OPT_MODE_GROUP].type = SANE_TYPE_GROUP;
 | |
|   s->opt[OPT_MODE_GROUP].cap = 0;
 | |
|   s->opt[OPT_MODE_GROUP].constraint_type = SANE_CONSTRAINT_NONE;
 | |
| 
 | |
|   /* resolution */
 | |
|   s->opt[OPT_RESOLUTION].name = SANE_NAME_SCAN_RESOLUTION;
 | |
|   s->opt[OPT_RESOLUTION].title = SANE_TITLE_SCAN_RESOLUTION;
 | |
|   s->opt[OPT_RESOLUTION].desc = SANE_DESC_SCAN_RESOLUTION;
 | |
|   s->opt[OPT_RESOLUTION].type = SANE_TYPE_STRING;
 | |
|   s->opt[OPT_RESOLUTION].cap |= SANE_CAP_INACTIVE;
 | |
|   s->opt[OPT_RESOLUTION].size = 5;      /* sizeof("High") */
 | |
|   s->opt[OPT_RESOLUTION].unit = SANE_UNIT_NONE;
 | |
|   s->opt[OPT_RESOLUTION].constraint_type = SANE_CONSTRAINT_STRING_LIST;
 | |
|   s->opt[OPT_RESOLUTION].constraint.string_list = resolution_list;
 | |
|   s->val[OPT_RESOLUTION].s = strdup (resolution_list[V4L_RES_LOW]);
 | |
|   s->val[OPT_RESOLUTION].w = 1;
 | |
| 
 | |
|   /* bit-depth */
 | |
|   s->opt[OPT_DEPTH].name = SANE_NAME_BIT_DEPTH;
 | |
|   s->opt[OPT_DEPTH].title = "Pixel depth";
 | |
|   s->opt[OPT_DEPTH].desc = "Number of bits per pixel.";
 | |
|   s->opt[OPT_DEPTH].type = SANE_TYPE_INT;
 | |
|   s->opt[OPT_DEPTH].unit = SANE_UNIT_BIT;
 | |
|   s->opt[OPT_DEPTH].constraint_type = SANE_CONSTRAINT_WORD_LIST;
 | |
|   s->opt[OPT_DEPTH].constraint.word_list = color_depth_list;
 | |
|   s->val[OPT_DEPTH].w = pict.depth;
 | |
|   if (pict.depth == 0)
 | |
|      s->val[OPT_DEPTH].w = 8;
 | |
| 
 | |
|   /* test */
 | |
|   s->opt[OPT_TEST].name = "test-image";
 | |
|   s->opt[OPT_TEST].title = "Force test image";
 | |
|   s->opt[OPT_TEST].desc =
 | |
|     "Acquire a test-image instead of the image seen by the camera. "
 | |
|     "The test image consists of red, green, and blue squares of "
 | |
|     "32x32 pixels each.  Use this to find out whether the "
 | |
|     "camera is connected properly.";
 | |
|   s->opt[OPT_TEST].type = SANE_TYPE_BOOL;
 | |
|   s->val[OPT_TEST].w = SANE_FALSE;
 | |
| 
 | |
|   /* "Geometry" group: */
 | |
|   s->opt[OPT_GEOMETRY_GROUP].title = "Geometry";
 | |
|   s->opt[OPT_GEOMETRY_GROUP].desc = "";
 | |
|   s->opt[OPT_GEOMETRY_GROUP].type = SANE_TYPE_GROUP;
 | |
|   s->opt[OPT_GEOMETRY_GROUP].cap = SANE_CAP_ADVANCED;
 | |
|   s->opt[OPT_GEOMETRY_GROUP].constraint_type = SANE_CONSTRAINT_NONE;
 | |
| 
 | |
|   /* top-left x */
 | |
|   s->opt[OPT_TL_X].name = SANE_NAME_SCAN_TL_X;
 | |
|   s->opt[OPT_TL_X].title = SANE_TITLE_SCAN_TL_X;
 | |
|   s->opt[OPT_TL_X].desc = SANE_DESC_SCAN_TL_X;
 | |
|   s->opt[OPT_TL_X].type = SANE_TYPE_INT;
 | |
|   s->opt[OPT_TL_X].unit = SANE_UNIT_PIXEL;
 | |
|   s->opt[OPT_TL_X].constraint_type = SANE_CONSTRAINT_RANGE;
 | |
|   s->opt[OPT_TL_X].constraint.range = &x_range;
 | |
|   s->val[OPT_TL_X].w = 0;
 | |
| 
 | |
|   /* top-left y */
 | |
|   s->opt[OPT_TL_Y].name = SANE_NAME_SCAN_TL_Y;
 | |
|   s->opt[OPT_TL_Y].title = SANE_TITLE_SCAN_TL_Y;
 | |
|   s->opt[OPT_TL_Y].desc = SANE_DESC_SCAN_TL_Y;
 | |
|   s->opt[OPT_TL_Y].type = SANE_TYPE_INT;
 | |
|   s->opt[OPT_TL_Y].unit = SANE_UNIT_PIXEL;
 | |
|   s->opt[OPT_TL_Y].constraint_type = SANE_CONSTRAINT_RANGE;
 | |
|   s->opt[OPT_TL_Y].constraint.range = &y_range;
 | |
|   s->val[OPT_TL_Y].w = 0;
 | |
| 
 | |
|   /* bottom-right x */
 | |
|   s->opt[OPT_BR_X].name = SANE_NAME_SCAN_BR_X;
 | |
|   s->opt[OPT_BR_X].title = SANE_TITLE_SCAN_BR_X;
 | |
|   s->opt[OPT_BR_X].desc = SANE_DESC_SCAN_BR_X;
 | |
|   s->opt[OPT_BR_X].type = SANE_TYPE_INT;
 | |
|   s->opt[OPT_BR_X].unit = SANE_UNIT_PIXEL;
 | |
|   s->opt[OPT_BR_X].constraint_type = SANE_CONSTRAINT_RANGE;
 | |
|   s->opt[OPT_BR_X].constraint.range = &odd_x_range;
 | |
|   s->val[OPT_BR_X].w = capability.maxwidth;
 | |
|   if (s->val[OPT_BR_X].w > 767)
 | |
|     s->val[OPT_BR_X].w = 767;
 | |
| 
 | |
|   /* bottom-right y */
 | |
|   s->opt[OPT_BR_Y].name = SANE_NAME_SCAN_BR_Y;
 | |
|   s->opt[OPT_BR_Y].title = SANE_TITLE_SCAN_BR_Y;
 | |
|   s->opt[OPT_BR_Y].desc = SANE_DESC_SCAN_BR_Y;
 | |
|   s->opt[OPT_BR_Y].type = SANE_TYPE_INT;
 | |
|   s->opt[OPT_BR_Y].unit = SANE_UNIT_PIXEL;
 | |
|   s->opt[OPT_BR_Y].constraint_type = SANE_CONSTRAINT_RANGE;
 | |
|   s->opt[OPT_BR_Y].constraint.range = &odd_y_range;
 | |
|   s->val[OPT_BR_Y].w = capability.maxheight;
 | |
|   if (s->val[OPT_BR_Y].w > 511)
 | |
|     s->val[OPT_BR_Y].w = 511;
 | |
| 
 | |
|   /* xfer-scale */
 | |
|   s->opt[OPT_XFER_SCALE].name = "transfer-scale";
 | |
|   s->opt[OPT_XFER_SCALE].title = "Transfer scale";
 | |
|   s->opt[OPT_XFER_SCALE].desc =
 | |
|     "The transferscale determines how many of the acquired pixels actually "
 | |
|     "get sent to the computer.  For example, a transfer scale of 2 would "
 | |
|     "request that every other acquired pixel would be omitted.  That is, "
 | |
|     "when scanning a 200 pixel wide and 100 pixel tall area, the resulting "
 | |
|     "image would be only 100x50 pixels large.  Using a large transfer scale "
 | |
|     "improves acquisition speed, but reduces resolution.";
 | |
|   s->opt[OPT_XFER_SCALE].type = SANE_TYPE_INT;
 | |
|   s->opt[OPT_XFER_SCALE].constraint_type = SANE_CONSTRAINT_WORD_LIST;
 | |
|   s->opt[OPT_XFER_SCALE].constraint.word_list = xfer_scale_list;
 | |
|   s->val[OPT_XFER_SCALE].w = xfer_scale_list[1];
 | |
| 
 | |
|   /* despeckle */
 | |
|   s->opt[OPT_DESPECKLE].name = "despeckle";
 | |
|   s->opt[OPT_DESPECKLE].title = "Speckle filter";
 | |
|   s->opt[OPT_DESPECKLE].desc = "Turning on this filter will remove the "
 | |
|     "christmas lights that are typically present in dark images.";
 | |
|   s->opt[OPT_DESPECKLE].type = SANE_TYPE_BOOL;
 | |
|   s->opt[OPT_DESPECKLE].constraint_type = SANE_CONSTRAINT_NONE;
 | |
|   s->val[OPT_DESPECKLE].w = 0;
 | |
| 
 | |
| 
 | |
|   /* "Enhancement" group: */
 | |
| 
 | |
|   s->opt[OPT_ENHANCEMENT_GROUP].title = "Enhancement";
 | |
|   s->opt[OPT_ENHANCEMENT_GROUP].desc = "";
 | |
|   s->opt[OPT_ENHANCEMENT_GROUP].type = SANE_TYPE_GROUP;
 | |
|   s->opt[OPT_ENHANCEMENT_GROUP].cap = 0;
 | |
|   s->opt[OPT_ENHANCEMENT_GROUP].constraint_type = SANE_CONSTRAINT_NONE;
 | |
| 
 | |
|   /* brightness */
 | |
|   s->opt[OPT_BRIGHTNESS].name = SANE_NAME_BRIGHTNESS;
 | |
|   s->opt[OPT_BRIGHTNESS].title = SANE_TITLE_BRIGHTNESS;
 | |
|   s->opt[OPT_BRIGHTNESS].desc = SANE_DESC_BRIGHTNESS
 | |
|     "This controls the brightness of the Picture returned from"
 | |
|     "the video4linux device.";
 | |
|   s->opt[OPT_BRIGHTNESS].type = SANE_TYPE_INT;
 | |
|   s->opt[OPT_BRIGHTNESS].cap |= SANE_CAP_AUTOMATIC;
 | |
|   s->opt[OPT_BRIGHTNESS].constraint_type = SANE_CONSTRAINT_RANGE;
 | |
|   s->opt[OPT_BRIGHTNESS].constraint.range = &brightness_range;
 | |
|   s->val[OPT_BRIGHTNESS].w = pict.brightness / 256;
 | |
| 
 | |
|   /* contrast */
 | |
|   s->opt[OPT_CONTRAST].name = SANE_NAME_CONTRAST;
 | |
|   s->opt[OPT_CONTRAST].title = SANE_TITLE_CONTRAST;
 | |
|   s->opt[OPT_CONTRAST].desc = SANE_DESC_CONTRAST
 | |
|     " This controls the contrast of the Picture returned from"
 | |
|     "the video4linux device";
 | |
|   s->opt[OPT_CONTRAST].type = SANE_TYPE_INT;
 | |
|   s->opt[OPT_CONTRAST].constraint_type = SANE_CONSTRAINT_RANGE;
 | |
|   s->opt[OPT_CONTRAST].constraint.range = &u8_range;
 | |
|   s->val[OPT_CONTRAST].w = pict.contrast /256;
 | |
| 
 | |
|   /* black-level */
 | |
|   s->opt[OPT_BLACK_LEVEL].name = SANE_NAME_BLACK_LEVEL;
 | |
|   s->opt[OPT_BLACK_LEVEL].title = SANE_TITLE_BLACK_LEVEL;
 | |
|   s->opt[OPT_BLACK_LEVEL].desc = SANE_DESC_BLACK_LEVEL
 | |
|     " This value should be selected so that black areas just start "
 | |
|     "to look really black (not gray).";
 | |
|   s->opt[OPT_BLACK_LEVEL].type = SANE_TYPE_INT;
 | |
|   s->opt[OPT_BLACK_LEVEL].constraint_type = SANE_CONSTRAINT_RANGE;
 | |
|   s->opt[OPT_BLACK_LEVEL].constraint.range = &u8_range;
 | |
|   s->val[OPT_BLACK_LEVEL].w = 0;
 | |
| 
 | |
|   /* white-level */
 | |
|   s->opt[OPT_WHITE_LEVEL].name = SANE_NAME_WHITE_LEVEL;
 | |
|   s->opt[OPT_WHITE_LEVEL].title = SANE_TITLE_WHITE_LEVEL;
 | |
|   s->opt[OPT_WHITE_LEVEL].desc = SANE_DESC_WHITE_LEVEL
 | |
|     " This value should be selected so that white areas just start "
 | |
|     "to look really white (not gray).";
 | |
|   s->opt[OPT_WHITE_LEVEL].type = SANE_TYPE_INT;
 | |
|   s->opt[OPT_WHITE_LEVEL].constraint_type = SANE_CONSTRAINT_RANGE;
 | |
|   s->opt[OPT_WHITE_LEVEL].constraint.range = &u8_range;
 | |
|   s->val[OPT_WHITE_LEVEL].w = pict.whiteness /256;
 | |
| 
 | |
|   /* hue */
 | |
|   s->opt[OPT_HUE].name = SANE_NAME_HUE;
 | |
|   s->opt[OPT_HUE].title = SANE_TITLE_HUE;
 | |
|   s->opt[OPT_HUE].desc = SANE_DESC_HUE
 | |
|     "This value should be selected so that pink looks realy pink";
 | |
|   s->opt[OPT_HUE].type = SANE_TYPE_INT;
 | |
|   s->opt[OPT_HUE].constraint_type = SANE_CONSTRAINT_RANGE;
 | |
|   s->opt[OPT_HUE].constraint.range = &u8_range;
 | |
|   s->val[OPT_HUE].w = pict.hue / 256;
 | |
| 
 | |
|   /* saturation */
 | |
|   s->opt[OPT_SATURATION].name = SANE_NAME_SATURATION;
 | |
|   s->opt[OPT_SATURATION].title = SANE_TITLE_SATURATION;
 | |
|   s->opt[OPT_SATURATION].desc = SANE_DESC_SATURATION
 | |
|     "This value controlls wether the Picture looks Black and White"
 | |
|     "or shows to much color";
 | |
|   s->opt[OPT_SATURATION].type = SANE_TYPE_INT;
 | |
|   s->opt[OPT_SATURATION].constraint_type = SANE_CONSTRAINT_RANGE;
 | |
|   s->opt[OPT_SATURATION].constraint.range = &u8_range;
 | |
|   s->val[OPT_SATURATION].w = pict.colour / 256 ;
 | |
| 
 | |
|   return SANE_STATUS_GOOD;
 | |
| }
 | |
| 
 | |
| SANE_Status
 | |
| sane_init (SANE_Int *version_code, SANE_Auth_Callback authorize)
 | |
| {
 | |
|   char dev_name[PATH_MAX], * str;
 | |
|   size_t len;
 | |
|   FILE *fp;
 | |
| 
 | |
|   DBG_INIT();
 | |
| 
 | |
|   if (version_code)
 | |
|     *version_code = SANE_VERSION_CODE (V_MAJOR, V_MINOR, 0);
 | |
| 
 | |
|   fp = sanei_config_open (V4L_CONFIG_FILE);
 | |
|   if (!fp)
 | |
|     {
 | |
|       DBG(1, "sane_init: file `%s' not accessible\n", V4L_CONFIG_FILE);
 | |
|       return SANE_STATUS_INVAL;
 | |
|     }
 | |
| 
 | |
|   while (sanei_config_read(dev_name, sizeof (dev_name), fp))
 | |
|     {
 | |
|       if (dev_name[0] == '#')           /* ignore line comments */
 | |
|         continue;
 | |
|       len = strlen (dev_name);
 | |
| 
 | |
|       if (!len)
 | |
|         continue;                       /* ignore empty lines */
 | |
| 
 | |
|       /* Remove trailing space and trailing comments */
 | |
|       for (str = dev_name; *str && !isspace (*str) && *str != '#'; ++str);
 | |
|       *str = '\0';
 | |
|       v4ldev = open ( dev_name,O_RDWR);
 | |
|       if (-1 != v4ldev)
 | |
|         {
 | |
|           if (-1 != ioctl(v4ldev,VIDIOCGCAP,&capability))
 | |
|             {
 | |
|               DBG(1, "sane_init: found videodev on `%s'\n", dev_name);
 | |
|               attach (dev_name, 0);
 | |
|             }
 | |
|           else
 | |
|             DBG(1, "sane_init: ioctl(%d,VIDIOCGCAP,..) failed on `%s'\n",
 | |
|                 v4ldev, dev_name);
 | |
|           close (v4ldev);
 | |
|         }
 | |
|       else
 | |
|         DBG(1, "sane_init: failed to open device `%s'\n", dev_name);
 | |
|     }
 | |
|   fclose (fp);
 | |
|   return SANE_STATUS_GOOD;
 | |
| }
 | |
| 
 | |
| void
 | |
| sane_exit (void)
 | |
| {
 | |
|   V4L_Device *dev, *next;
 | |
| 
 | |
|   for (dev = first_dev; dev; dev = next)
 | |
|     {
 | |
|       next = dev->next;
 | |
|       free ((void *) dev->sane.name);
 | |
|       free (dev);
 | |
|     }
 | |
| 
 | |
|   if (NULL != devlist)
 | |
|     {
 | |
|       free(devlist);
 | |
|       devlist = NULL;
 | |
|     }
 | |
| }
 | |
| 
 | |
| SANE_Status
 | |
| sane_get_devices (const SANE_Device *** device_list, SANE_Bool local_only)
 | |
| {
 | |
|   V4L_Device *dev;
 | |
|   int i;
 | |
| 
 | |
|   local_only = TRUE; /* Avoid compile warning */
 | |
| 
 | |
|   if (devlist)
 | |
|     free (devlist);
 | |
| 
 | |
|   devlist = malloc ((num_devices + 1) * sizeof (devlist[0]));
 | |
|   if (!devlist)
 | |
|     return SANE_STATUS_NO_MEM;
 | |
| 
 | |
|   i = 0;
 | |
|   for (dev = first_dev; i < num_devices; dev = dev->next)
 | |
|     devlist[i++] = &dev->sane;
 | |
|   devlist[i++] = 0;
 | |
| 
 | |
|   *device_list = devlist;
 | |
|   return SANE_STATUS_GOOD;
 | |
| }
 | |
| 
 | |
| SANE_Status
 | |
| sane_open (SANE_String_Const devicename, SANE_Handle *handle)
 | |
| {
 | |
|   SANE_Status status;
 | |
|   V4L_Device *dev;
 | |
|   V4L_Scanner *s;
 | |
| 
 | |
|   DBG(4, "open(%s)\n", devicename);
 | |
| 
 | |
|   if (devicename[0])
 | |
|     {
 | |
|       v4ldev = open ( devicename,O_RDWR);
 | |
|       if (-1 == v4ldev)
 | |
|         return SANE_STATUS_DEVICE_BUSY;
 | |
|       if (-1 == ioctl(v4ldev,VIDIOCGCAP,&capability))
 | |
|         return SANE_STATUS_INVAL;
 | |
|       status = attach (devicename, &dev);
 | |
|       if (status != SANE_STATUS_GOOD)
 | |
|         return status;
 | |
|       /*if (VID_TYPE_CAPTURE & capability.type)
 | |
|         syslog (LOG_NOTICE,"V4L Device can Capture");
 | |
|       if (VID_TYPE_TUNER & capability.type)
 | |
|         syslog (LOG_NOTICE,"V4L Device has a Tuner");
 | |
|       if (VID_TYPE_TELETEXT & capability.type)
 | |
|         syslog (LOG_NOTICE,"V4L Device supports Teletext");
 | |
|       if (VID_TYPE_OVERLAY & capability.type)
 | |
|         syslog (LOG_NOTICE,"V4L Device supports Overlay");
 | |
|       if (VID_TYPE_CHROMAKEY & capability.type)
 | |
|         syslog (LOG_NOTICE,"V4L Device uses Chromakey");
 | |
|       if (VID_TYPE_CLIPPING & capability.type)
 | |
|         syslog (LOG_NOTICE,"V4L Device supports Clipping");
 | |
|       if (VID_TYPE_FRAMERAM & capability.type)
 | |
|         syslog (LOG_NOTICE,"V4L Device overwrites Framebuffermemory");
 | |
|       if (VID_TYPE_SCALES & capability.type)
 | |
|         syslog (LOG_NOTICE,"V4L Device supports Hardware-scalling");
 | |
|       if (VID_TYPE_MONOCHROME & capability.type)
 | |
|         syslog (LOG_NOTICE,"V4L Device is Monochrome");
 | |
|       if (VID_TYPE_SUBCAPTURE & capability.type)
 | |
|         syslog (LOG_NOTICE,"V4L Device can capture parts of the Image");
 | |
| */
 | |
|       if (-1 == ioctl(v4ldev,VIDIOCGPICT,&pict))
 | |
|         return SANE_STATUS_INVAL;
 | |
| 
 | |
|       if (-1 == ioctl(v4ldev,VIDIOCGWIN,&window))
 | |
|         return SANE_STATUS_INVAL;
 | |
| 
 | |
|       if (-1 == ioctl(v4ldev,VIDIOCGMBUF,&ov_mbuf))
 | |
|         DBG(1, "No Fbuffer\n");
 | |
|     }
 | |
|   else
 | |
|     /* empty devicname -> use first device */
 | |
|     dev = first_dev;
 | |
| 
 | |
|   if (!dev)
 | |
|     return SANE_STATUS_INVAL;
 | |
| 
 | |
|   s = malloc (sizeof (*s));
 | |
|   if (!s)
 | |
|     return SANE_STATUS_NO_MEM;
 | |
|   memset (s, 0, sizeof (*s));
 | |
|   s->user_corner = 0;
 | |
|   s->value_changed = ~0;        /* ensure all options get updated */
 | |
|   s->devicename = devicename;
 | |
|   s->fd = v4ldev;
 | |
|   init_options (s);
 | |
| 
 | |
|   /* insert newly opened handle into list of open handles: */
 | |
|   s->next = first_handle;
 | |
|   first_handle = s;
 | |
| 
 | |
|   *handle = s;
 | |
| 
 | |
|   return SANE_STATUS_GOOD;
 | |
| }
 | |
| 
 | |
| void
 | |
| sane_close (SANE_Handle handle)
 | |
| {
 | |
|   V4L_Scanner *prev, *s;
 | |
| 
 | |
|   /* remove handle from list of open handles: */
 | |
|   prev = 0;
 | |
|   for (s = first_handle; s; s = s->next)
 | |
|     {
 | |
|       if (s == handle)
 | |
|         break;
 | |
|       prev = s;
 | |
|     }
 | |
|   if (!s)
 | |
|     {
 | |
|       DBG(1, "sane_close: bad handle %p\n", handle);
 | |
|       return;           /* oops, not a handle we know about */
 | |
|     }
 | |
|   if (prev)
 | |
|     prev->next = s->next;
 | |
|   else
 | |
|     first_handle = s->next;
 | |
| 
 | |
|   if (s->scanning)
 | |
|     sane_cancel (handle);
 | |
|   close(s->fd);
 | |
|   free (s);
 | |
| }
 | |
| 
 | |
| const SANE_Option_Descriptor *
 | |
| sane_get_option_descriptor (SANE_Handle handle, SANE_Int option)
 | |
| {
 | |
|   V4L_Scanner *s = handle;
 | |
| 
 | |
|   if ((unsigned) option >= NUM_OPTIONS)
 | |
|     return 0;
 | |
|   return s->opt + option;
 | |
| }
 | |
| 
 | |
| SANE_Status
 | |
| sane_control_option (SANE_Handle handle, SANE_Int option,
 | |
|                      SANE_Action action, void *val, SANE_Int *info)
 | |
| {
 | |
|   V4L_Scanner *s = handle;
 | |
|   SANE_Status status;
 | |
|   SANE_Word cap;
 | |
| 
 | |
|   if (info)
 | |
|     *info = 0;
 | |
| 
 | |
|   if (option >= NUM_OPTIONS)
 | |
|     return SANE_STATUS_INVAL;
 | |
| 
 | |
|   cap = s->opt[option].cap;
 | |
| 
 | |
|   if (!SANE_OPTION_IS_ACTIVE (cap))
 | |
|     return SANE_STATUS_INVAL;
 | |
| 
 | |
|   if (action == SANE_ACTION_GET_VALUE)
 | |
|     {
 | |
|       init_options(s);
 | |
|       switch (option)
 | |
|         {
 | |
|           /* word options: */
 | |
|         case OPT_NUM_OPTS:
 | |
|         case OPT_DEPTH:
 | |
|         case OPT_DESPECKLE:
 | |
|         case OPT_TEST:
 | |
|         case OPT_TL_X:
 | |
|         case OPT_TL_Y:
 | |
|         case OPT_BR_X:
 | |
|         case OPT_BR_Y:
 | |
|         case OPT_XFER_SCALE:
 | |
|         case OPT_BRIGHTNESS:
 | |
|         case OPT_CONTRAST:
 | |
|         case OPT_BLACK_LEVEL:
 | |
|         case OPT_WHITE_LEVEL:
 | |
|         case OPT_HUE:
 | |
|         case OPT_SATURATION:
 | |
|           *(SANE_Word *) val = s->val[option].w;
 | |
|           return SANE_STATUS_GOOD;
 | |
|         default:
 | |
|           DBG(1, "control_option: option %d unknown\n", option);
 | |
|         }
 | |
|     }
 | |
|   else if (action == SANE_ACTION_SET_VALUE)
 | |
|     {
 | |
|       if (!SANE_OPTION_IS_SETTABLE (cap))
 | |
|         {
 | |
|         return SANE_STATUS_INVAL;
 | |
|         }
 | |
|       status = sanei_constrain_value (s->opt + option, val, info);
 | |
|       if (status != SANE_STATUS_GOOD)
 | |
|         return status;
 | |
| 
 | |
|       if (-1 == ioctl(s->fd,VIDIOCGWIN,&window))
 | |
|         {
 | |
|           DBG(1, "Can not get Window Geometry\n");
 | |
|           /*return SANE_STATUS_INVAL;*/
 | |
|         }
 | |
|       window.clipcount  = 0;
 | |
|       window.clips      = 0;
 | |
|       window.height     = parms.lines;
 | |
|       window.width      = parms.pixels_per_line;
 | |
| 
 | |
|       if (option >= OPT_TL_X && option <= OPT_BR_Y)
 | |
|         {
 | |
|         s->user_corner |= 1 << (option - OPT_TL_X);
 | |
|         }
 | |
|       assert (option <= 31);
 | |
|       s->value_changed |= 1 << option;
 | |
| 
 | |
|       switch (option)
 | |
|         {
 | |
|           /* (mostly) side-effect-free word options: */
 | |
|         case OPT_TL_X:
 | |
|                 break;
 | |
|         case OPT_TL_Y:
 | |
|                 break;
 | |
|         case OPT_BR_X:
 | |
|                 window.width    = *(SANE_Word *)val;
 | |
|                 parms.pixels_per_line   = *(SANE_Word *)val;
 | |
|                 if (info)
 | |
|                   *info         |= SANE_INFO_RELOAD_PARAMS;
 | |
|                 break;
 | |
|         case OPT_BR_Y:
 | |
|                 window.height   = *(SANE_Word *)val;
 | |
|                 parms.lines     = *(SANE_Word *)val;
 | |
|                 if (info)
 | |
|                   *info         |= SANE_INFO_RELOAD_PARAMS;
 | |
|                 break;
 | |
|         case OPT_XFER_SCALE:
 | |
|                 break;
 | |
|         case OPT_DEPTH:
 | |
|                 pict.depth      = *(SANE_Word *)val;
 | |
|                 if (info)
 | |
|                   *info         |= SANE_INFO_RELOAD_PARAMS;
 | |
|                 break;
 | |
|           if (!s->scanning && info && s->val[option].w != *(SANE_Word *) val)
 | |
|             /* only signal the reload params if we're not scanning---no point
 | |
|                in creating the frontend useless work */
 | |
|             if (info)
 | |
|               *info |= SANE_INFO_RELOAD_PARAMS;
 | |
|           /* fall through */
 | |
|         case OPT_NUM_OPTS:
 | |
|         case OPT_TEST:
 | |
|         case OPT_DESPECKLE:
 | |
|         case OPT_BRIGHTNESS:
 | |
|                 pict.brightness = *(SANE_Word *)val * 256;
 | |
|                 if (info)
 | |
|                   *info         |= SANE_INFO_RELOAD_PARAMS;
 | |
|                 break;
 | |
|         case OPT_CONTRAST:
 | |
|                 pict.contrast   = *(SANE_Word *)val * 256;
 | |
|                 /* if (info) *info         |= SANE_INFO_RELOAD_PARAMS;*/
 | |
|                 break;
 | |
|         case OPT_BLACK_LEVEL:
 | |
|         case OPT_WHITE_LEVEL:
 | |
|                 pict.whiteness  = *(SANE_Word *)val * 256;
 | |
|                 if (info)
 | |
|                   *info         |= SANE_INFO_RELOAD_PARAMS;
 | |
|                 break;
 | |
|         case OPT_HUE:
 | |
|         case OPT_SATURATION:
 | |
|           s->val[option].w = *(SANE_Word *) val;
 | |
|         }
 | |
|       if (-1 == ioctl(s->fd,VIDIOCSWIN,&window))
 | |
|         {
 | |
|           DBG(1, "Can not set Window Geometry\n");
 | |
|           /*return SANE_STATUS_INVAL;*/
 | |
|         }
 | |
|       if (-1 == ioctl(s->fd,VIDIOCGWIN,&window))
 | |
|         {
 | |
|           DBG(1, "Can not get window geometry\n");
 | |
|         }
 | |
|       if (-1 == ioctl(s->fd,VIDIOCSPICT,&pict))
 | |
|         {
 | |
|           DBG(1, "Can not set Picture Parameters\n");
 | |
|           /*return SANE_STATUS_INVAL;*/
 | |
|         }
 | |
|       return SANE_STATUS_GOOD;
 | |
|     }
 | |
|   else if (action == SANE_ACTION_SET_AUTO)
 | |
|     {
 | |
|       switch (option)
 | |
|         {
 | |
|         case OPT_BRIGHTNESS:
 | |
|           /* not implemented yet */
 | |
|           return SANE_STATUS_GOOD;
 | |
| 
 | |
|         default:
 | |
|           break;
 | |
|         }
 | |
|     }
 | |
|   return SANE_STATUS_INVAL;
 | |
| }
 | |
| 
 | |
| SANE_Status
 | |
| sane_get_parameters (SANE_Handle handle, SANE_Parameters *params)
 | |
| {
 | |
|   V4L_Scanner *s = handle;
 | |
| 
 | |
|   if (-1 == ioctl(s->fd,VIDIOCGWIN,&window))
 | |
|      return SANE_STATUS_INVAL;
 | |
|   parms.pixels_per_line = window.width;
 | |
|   parms.bytes_per_line  = window.width;
 | |
|   if (parms.format == SANE_FRAME_RGB)
 | |
|     parms.bytes_per_line = window.width * 3;
 | |
|   parms.lines           = window.height;
 | |
|   *params = parms;
 | |
|   return SANE_STATUS_GOOD;
 | |
| 
 | |
| }
 | |
| 
 | |
| SANE_Status
 | |
| sane_start (SANE_Handle handle)
 | |
| {
 | |
|   int len;
 | |
|   V4L_Scanner *s;
 | |
| 
 | |
|   for (s = first_handle; s; s = s->next)
 | |
|     {
 | |
|       if (s == handle)
 | |
|         break;
 | |
|     }
 | |
|   if (!s)
 | |
|     {
 | |
|       DBG(1, "sane_start: bad handle %p\n", handle);
 | |
|       return SANE_STATUS_INVAL;         /* oops, not a handle we know about */
 | |
|     }
 | |
|   len = ioctl(v4ldev,VIDIOCGCAP,&capability);
 | |
|   if (-1 == len)
 | |
|     DBG(1, "Can not get capabilities\n");
 | |
|   buffercount = 0;
 | |
|   DBG(2, "sane_start\n");
 | |
|   if (-1 == ioctl(s->fd,VIDIOCGMBUF,&ov_mbuf))
 | |
|      {
 | |
|         s->mmap = FALSE;
 | |
|         buffer = malloc(capability.maxwidth * capability.maxheight * pict.depth);
 | |
|         if (0 == buffer)
 | |
|           return SANE_STATUS_NO_MEM;
 | |
|         DBG(2, "V4L READING Frame\n");
 | |
|         len = read (s->fd,buffer,parms.bytes_per_line * parms.lines);
 | |
|         DBG(2, "V4L Frame read\n");
 | |
|      }
 | |
|   else
 | |
|      {
 | |
|         s->mmap = TRUE;
 | |
|         DBG(2, "MMAP Frame\n");
 | |
|         DBG(2, "Buffersize %d , Buffers %d , Offset %d\n",
 | |
|             ov_mbuf.size,ov_mbuf.frames,ov_mbuf.offsets);
 | |
|         buffer = mmap (0,ov_mbuf.size,PROT_READ|PROT_WRITE,MAP_SHARED,s->fd,0);
 | |
|         DBG(2, "MMAPed Frame, Capture 1 Pict into %x\n",buffer);
 | |
|         gb.frame = 0;
 | |
|         gb.width = window.width;
 | |
|         gb.width = parms.pixels_per_line;
 | |
|         gb.height = window.height;
 | |
|         gb.height = parms.lines;
 | |
|         gb.format = pict.palette;
 | |
|         DBG(2,"MMAPed Frame %d x %d with Palette %d\n",
 | |
|             gb.width,gb.height,gb.format);
 | |
|         len = ioctl(v4ldev,VIDIOCMCAPTURE,&gb);
 | |
|         DBG(2, "mmap-result %d\n",len);
 | |
|         DBG(2, "Frame %x\n",gb.frame);
 | |
| #if 0
 | |
|         len = ioctl(v4ldev,VIDIOCSYNC,&(gb.frame));
 | |
|         if (-1 == len)
 | |
|           {
 | |
|             DBG(1, "Call to ioctl(%d, VIDIOCSYNC, ..) failed\n", v4ldev);
 | |
|             return SANE_STATUS_INVAL;
 | |
|           }
 | |
| #endif
 | |
|      }
 | |
|   DBG(2, "sane_start: done\n");
 | |
|   return SANE_STATUS_GOOD;
 | |
| }
 | |
| 
 | |
| SANE_Status
 | |
| sane_read (SANE_Handle handle, SANE_Byte *buf, SANE_Int max_len,
 | |
|            SANE_Int *lenp)
 | |
| {
 | |
|   int i,min;
 | |
|   V4L_Scanner *s = handle;
 | |
| 
 | |
|   if ((buffercount + 1) > (parms.lines * parms.bytes_per_line))
 | |
|      {
 | |
|         *lenp = 0;
 | |
|         return SANE_STATUS_EOF;
 | |
|      };
 | |
|   min = parms.lines * parms.bytes_per_line;
 | |
|   if (min > (max_len + buffercount))
 | |
|      min = (max_len + buffercount);
 | |
|   if (s->mmap == FALSE)
 | |
|      {
 | |
|           for (i=buffercount;i<(min+0);i++)
 | |
|              {
 | |
|                 *(buf + i - buffercount) = *(buffer +i);
 | |
|              };
 | |
|           *lenp = (parms.lines * parms.bytes_per_line - buffercount);
 | |
|           if (max_len < *lenp)
 | |
|              *lenp = max_len;
 | |
|           buffercount = i ;
 | |
|      }
 | |
|   else
 | |
|      {
 | |
|           for (i=buffercount;i<(min+0);i++)
 | |
|              {
 | |
|                 *(buf + i - buffercount) = *(buffer +i);
 | |
|                 /*switch (i % 3)
 | |
|                   {
 | |
|                     case 0:
 | |
|                         *(buf + i - buffercount) = *(buffer + i + 0);
 | |
|                         break;
 | |
|                     case 1:
 | |
|                         *(buf + i - buffercount) = *(buffer + i);
 | |
|                         break;
 | |
|                     case 2:
 | |
|                         *(buf + i - buffercount) = *(buffer + i - 0);
 | |
|                   };*/
 | |
|              };
 | |
|           *lenp = (parms.lines * parms.bytes_per_line - buffercount);
 | |
|           if ((i - buffercount) < *lenp)
 | |
|              *lenp = (i-buffercount);
 | |
|          /* syslog(LOG_NOTICE,"Transfering %d Bytes",*lenp);
 | |
|           syslog(LOG_NOTICE,"Transfering from Byte %d to Byte %d",buffercount,i);*/
 | |
|           buffercount = i;
 | |
|      }
 | |
|   return SANE_STATUS_GOOD;
 | |
| }
 | |
| 
 | |
| void
 | |
| sane_cancel (SANE_Handle handle)
 | |
| {
 | |
|   V4L_Scanner *s = handle;
 | |
| 
 | |
|   if ((buffer != 0) && (s->mmap == FALSE))
 | |
|      free(buffer);
 | |
|   buffer = 0;
 | |
| }
 | |
| 
 | |
| 
 | |
| SANE_Status
 | |
| sane_set_io_mode (SANE_Handle handle, SANE_Bool non_blocking)
 | |
| {
 | |
|   /* Avoid compile warning */
 | |
|   handle = 0;
 | |
|   non_blocking = 0;
 | |
| 
 | |
|   return SANE_STATUS_UNSUPPORTED;
 | |
| }
 | |
| 
 | |
| SANE_Status
 | |
| sane_get_select_fd (SANE_Handle handle, SANE_Int * fd)
 | |
| {
 | |
|   /* Avoid compile warning */
 | |
|   handle = 0;
 | |
|   fd = 0;
 | |
| 
 | |
|   return SANE_STATUS_UNSUPPORTED;
 | |
| }
 |