kopia lustrzana https://gitlab.com/sane-project/backends
				
				
				
			
		
			
				
	
	
		
			2037 wiersze
		
	
	
		
			63 KiB
		
	
	
	
		
			C
		
	
	
			
		
		
	
	
			2037 wiersze
		
	
	
		
			63 KiB
		
	
	
	
		
			C
		
	
	
| /* sane - Scanner Access Now Easy.
 | |
| 
 | |
|    Copyright (C) 2009-12 Stéphane Voltz <stef.dev@free.fr>
 | |
| 
 | |
|    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.
 | |
| */
 | |
| /*   --------------------------------------------------------------------------
 | |
| 
 | |
| */
 | |
| 
 | |
| /* ------------------------------------------------------------------------- */
 | |
| /*! \mainpage Primax PagePartner Parallel Port scanner Index Page
 | |
|  *
 | |
|  * \section intro_sec Introduction
 | |
|  *
 | |
|  * This backend provides support for the Prima PagePartner sheet fed parallel
 | |
|  * port scanner.
 | |
|  *
 | |
|  * \section sane_api SANE API
 | |
|  *
 | |
|  * \subsection sane_flow sane flow
 | |
|    SANE FLOW
 | |
|    - sane_init() : initialize backend, attach scanners.
 | |
|    	- sane_get_devices() : query list of scanner devices, backend must
 | |
|                            	probe for new devices.
 | |
|    	- sane_open() : open a particular scanner device, adding a handle
 | |
|    		     	to the opened device
 | |
| 		- sane_set_io_mode() : set blocking mode
 | |
| 		- sane_get_select_fd() : get scanner fd
 | |
| 		- sane_get_option_descriptor() : get option information
 | |
| 		- sane_control_option() : change option values
 | |
| 		- sane_start() : start image acquisition
 | |
| 			- sane_get_parameters() : returns actual scan parameters for the ongoing scan
 | |
| 			- sane_read() : read image data
 | |
| 		- sane_cancel() : cancel operation, end scan
 | |
|    	- sane_close() : close opened scanner device, freeing scanner handle
 | |
|    - sane_exit() : terminate use of backend, freeing all resources for attached
 | |
|    		   devices when last frontend quits
 | |
|  */
 | |
| 
 | |
| /**
 | |
|  * the build number allow to know which version of the backend is running.
 | |
|  */
 | |
| #define BUILD 2301
 | |
| 
 | |
| #include "p5.h"
 | |
| 
 | |
| /**
 | |
|  * Import directly the low level part needed to
 | |
|  * operate scanner. The alternative is to prefix all public functions
 | |
|  * with sanei_p5_ ,and have all the functions prototyped in
 | |
|  * p5_device.h .
 | |
|  */
 | |
| #include "p5_device.c"
 | |
| 
 | |
| /**
 | |
|  * number of time the backend has been loaded by sane_init.
 | |
|  */
 | |
| static int init_count = 0;
 | |
| 
 | |
| /**
 | |
|  * NULL terminated list of opened frontend sessions. Sessions are
 | |
|  * inserted here on sane_open() and removed on sane_close().
 | |
|  */
 | |
| static P5_Session *sessions = NULL;
 | |
| 
 | |
| /**
 | |
|  * NULL terminated list of detected physical devices.
 | |
|  * The same device may be opened several time by different sessions.
 | |
|  * Entry are inserted here by the attach() function.
 | |
|  * */
 | |
| static P5_Device *devices = NULL;
 | |
| 
 | |
| /**
 | |
|  * NULL terminated list of devices needed by sane_get_devices(), since
 | |
|  * the result returned must stay consistent until next call.
 | |
|  */
 | |
| static const SANE_Device **devlist = 0;
 | |
| 
 | |
| /**
 | |
|  * list of possible color modes
 | |
|  */
 | |
| static SANE_String_Const mode_list[] = {
 | |
|   SANE_I18N (COLOR_MODE),
 | |
|   SANE_I18N (GRAY_MODE),
 | |
|   /* SANE_I18N (LINEART_MODE), not supported yet */
 | |
|   0
 | |
| };
 | |
| 
 | |
| static SANE_Range x_range = {
 | |
|   SANE_FIX (0.0),		/* minimum */
 | |
|   SANE_FIX (216.0),		/* maximum */
 | |
|   SANE_FIX (0.0)		/* quantization */
 | |
| };
 | |
| 
 | |
| static SANE_Range y_range = {
 | |
|   SANE_FIX (0.0),		/* minimum */
 | |
|   SANE_FIX (299.0),		/* maximum */
 | |
|   SANE_FIX (0.0)		/* no quantization */
 | |
| };
 | |
| 
 | |
| /**
 | |
|  * finds the maximum string length in a string array.
 | |
|  */
 | |
| static size_t
 | |
| max_string_size (const SANE_String_Const strings[])
 | |
| {
 | |
|   size_t size, max_size = 0;
 | |
|   SANE_Int i;
 | |
| 
 | |
|   for (i = 0; strings[i]; ++i)
 | |
|     {
 | |
|       size = strlen (strings[i]) + 1;
 | |
|       if (size > max_size)
 | |
| 	max_size = size;
 | |
|     }
 | |
|   return max_size;
 | |
| }
 | |
| 
 | |
| /**> placeholders for decoded configuration values */
 | |
| static P5_Config p5cfg;
 | |
| 
 | |
| 
 | |
| /* ------------------------------------------------------------------------- */
 | |
| 
 | |
| /*
 | |
|  * SANE Interface
 | |
|  */
 | |
| 
 | |
| 
 | |
| /**
 | |
|  * Called by SANE initially.
 | |
|  *
 | |
|  * From the SANE spec:
 | |
|  * This function must be called before any other SANE function can be
 | |
|  * called. The behavior of a SANE backend is undefined if this
 | |
|  * function is not called first. The version code of the backend is
 | |
|  * returned in the value pointed to by version_code. If that pointer
 | |
|  * is NULL, no version code is returned. Argument authorize is either
 | |
|  * a pointer to a function that is invoked when the backend requires
 | |
|  * authentication for a specific resource or NULL if the frontend does
 | |
|  * not support authentication.
 | |
|  */
 | |
| SANE_Status
 | |
| sane_init (SANE_Int * version_code, SANE_Auth_Callback authorize)
 | |
| {
 | |
|   SANE_Status status;
 | |
| 
 | |
|   authorize = authorize;	/* get rid of compiler warning */
 | |
| 
 | |
|   init_count++;
 | |
| 
 | |
|   /* init backend debug */
 | |
|   DBG_INIT ();
 | |
|   DBG (DBG_info, "SANE P5 backend version %d.%d-%d\n",
 | |
|        SANE_CURRENT_MAJOR, V_MINOR, BUILD);
 | |
|   DBG (DBG_proc, "sane_init: start\n");
 | |
|   DBG (DBG_trace, "sane_init: init_count=%d\n", init_count);
 | |
| 
 | |
|   if (version_code)
 | |
|     *version_code = SANE_VERSION_CODE (SANE_CURRENT_MAJOR, V_MINOR, BUILD);
 | |
| 
 | |
|   /* cold-plugging case : probe for already plugged devices */
 | |
|   status = probe_p5_devices ();
 | |
| 
 | |
|   DBG (DBG_proc, "sane_init: exit\n");
 | |
|   return status;
 | |
| }
 | |
| 
 | |
| 
 | |
| /**
 | |
|  * Called by SANE to find out about supported devices.
 | |
|  *
 | |
|  * From the SANE spec:
 | |
|  * This function can be used to query the list of devices that are
 | |
|  * available. If the function executes successfully, it stores a
 | |
|  * pointer to a NULL terminated array of pointers to SANE_Device
 | |
|  * structures in *device_list. The returned list is guaranteed to
 | |
|  * remain unchanged and valid until (a) another call to this function
 | |
|  * is performed or (b) a call to sane_exit() is performed. This
 | |
|  * function can be called repeatedly to detect when new devices become
 | |
|  * available. If argument local_only is true, only local devices are
 | |
|  * returned (devices directly attached to the machine that SANE is
 | |
|  * running on). If it is false, the device list includes all remote
 | |
|  * devices that are accessible to the SANE library.
 | |
|  *
 | |
|  * SANE does not require that this function is called before a
 | |
|  * sane_open() call is performed. A device name may be specified
 | |
|  * explicitly by a user which would make it unnecessary and
 | |
|  * undesirable to call this function first.
 | |
|  * @param device_list pointer where to store the device list
 | |
|  * @param local_only SANE_TRUE if only local devices are required.
 | |
|  * @return SANE_STATUS_GOOD when successfull
 | |
|  */
 | |
| SANE_Status
 | |
| sane_get_devices (const SANE_Device *** device_list, SANE_Bool local_only)
 | |
| {
 | |
|   int dev_num, devnr;
 | |
|   struct P5_Device *device;
 | |
|   SANE_Device *sane_device;
 | |
|   int i;
 | |
| 
 | |
|   DBG (DBG_proc, "sane_get_devices: start: local_only = %s\n",
 | |
|        local_only == SANE_TRUE ? "true" : "false");
 | |
| 
 | |
|   /* free existing devlist first */
 | |
|   if (devlist)
 | |
|     {
 | |
|       for (i = 0; devlist[i] != NULL; i++)
 | |
| 	free ((void *)devlist[i]);
 | |
|       free (devlist);
 | |
|       devlist = NULL;
 | |
|     }
 | |
| 
 | |
|   /**
 | |
|    * Since sane_get_devices() may be called repeatedly to detect new devices,
 | |
|    * the device detection must be run at each call. We are handling
 | |
|    * hot-plugging : we probe for devices plugged since sane_init() was called.
 | |
|    */
 | |
|   probe_p5_devices ();
 | |
| 
 | |
|   /* if no devices detected, just return an empty list */
 | |
|   if (devices == NULL)
 | |
|     {
 | |
|       devlist = malloc (sizeof (devlist[0]));
 | |
|       if (!devlist)
 | |
| 	return SANE_STATUS_NO_MEM;
 | |
|       devlist[0] = NULL;
 | |
|       *device_list = devlist;
 | |
|       DBG (DBG_proc, "sane_get_devices: exit with no device\n");
 | |
|       return SANE_STATUS_GOOD;
 | |
|     }
 | |
| 
 | |
|   /* count physical devices */
 | |
|   devnr = 1;
 | |
|   device = devices;
 | |
|   while (device->next)
 | |
|     {
 | |
|       devnr++;
 | |
|       device = device->next;
 | |
|     }
 | |
| 
 | |
|   /* allocate room for the list, plus 1 for the NULL terminator */
 | |
|   devlist = malloc ((devnr + 1) * sizeof (devlist[0]));
 | |
|   if (!devlist)
 | |
|     return SANE_STATUS_NO_MEM;
 | |
| 
 | |
|   *device_list = devlist;
 | |
| 
 | |
|   dev_num = 0;
 | |
|   device = devices;
 | |
| 
 | |
|   /* we build a list of SANE_Device from the list of attached devices */
 | |
|   for (i = 0; i < devnr; i++)
 | |
|     {
 | |
|       /* add device according to local only flag */
 | |
|       if ((local_only == SANE_TRUE && device->local == SANE_TRUE)
 | |
| 	  || local_only == SANE_FALSE)
 | |
| 	{
 | |
| 	  /* allocate memory to add the device */
 | |
| 	  sane_device = malloc (sizeof (*sane_device));
 | |
| 	  if (!sane_device)
 | |
| 	    {
 | |
| 	      return SANE_STATUS_NO_MEM;
 | |
| 	    }
 | |
| 
 | |
| 	  /* copy data */
 | |
| 	  sane_device->name = device->name;
 | |
| 	  sane_device->vendor = device->model->vendor;
 | |
| 	  sane_device->model = device->model->product;
 | |
| 	  sane_device->type = device->model->type;
 | |
| 	  devlist[dev_num] = sane_device;
 | |
| 
 | |
| 	  /* increment device counter */
 | |
| 	  dev_num++;
 | |
| 	}
 | |
| 
 | |
|       /* go to next detected device */
 | |
|       device = device->next;
 | |
|     }
 | |
|   devlist[dev_num] = 0;
 | |
| 
 | |
|   *device_list = devlist;
 | |
| 
 | |
|   DBG (DBG_proc, "sane_get_devices: exit\n");
 | |
| 
 | |
|   return SANE_STATUS_GOOD;
 | |
| }
 | |
| 
 | |
| 
 | |
| /**
 | |
|  * Called to establish connection with the session. This function will
 | |
|  * also establish meaningful defaults and initialize the options.
 | |
|  *
 | |
|  * From the SANE spec:
 | |
|  * This function is used to establish a connection to a particular
 | |
|  * device. The name of the device to be opened is passed in argument
 | |
|  * name. If the call completes successfully, a handle for the device
 | |
|  * is returned in *h. As a special case, specifying a zero-length
 | |
|  * string as the device requests opening the first available device
 | |
|  * (if there is such a device). Another special case is to only give
 | |
|  * the name of the backend as the device name, in this case the first
 | |
|  * available device will also be used.
 | |
|  * @param name name of the device to open
 | |
|  * @param handle opaque pointer where to store the pointer of
 | |
|  *        the opened P5_Session
 | |
|  * @return SANE_STATUS_GOOD on success
 | |
|  */
 | |
| SANE_Status
 | |
| sane_open (SANE_String_Const name, SANE_Handle * handle)
 | |
| {
 | |
|   struct P5_Session *session = NULL;
 | |
|   struct P5_Device *device = NULL;
 | |
| 
 | |
|   DBG (DBG_proc, "sane_open: start (devicename=%s)\n", name);
 | |
| 
 | |
|   /* check there is at least a device */
 | |
|   if (devices == NULL)
 | |
|     {
 | |
|       DBG (DBG_proc, "sane_open: exit, no device to open!\n");
 | |
|       return SANE_STATUS_INVAL;
 | |
|     }
 | |
| 
 | |
|   if (name[0] == 0 || strncmp (name, "p5", strlen ("p5")) == 0)
 | |
|     {
 | |
|       DBG (DBG_info,
 | |
| 	   "sane_open: no specific device requested, using default\n");
 | |
|       if (devices)
 | |
| 	{
 | |
| 	  device = devices;
 | |
| 	  DBG (DBG_info, "sane_open: device %s used as default device\n",
 | |
| 	       device->name);
 | |
| 	}
 | |
|     }
 | |
|   else
 | |
|     {
 | |
|       DBG (DBG_info, "sane_open: device %s requested\n", name);
 | |
|       /* walk the device list until we find a matching name */
 | |
|       device = devices;
 | |
|       while (device && strcmp (device->name, name) != 0)
 | |
| 	{
 | |
| 	  DBG (DBG_trace, "sane_open: device %s doesn't match\n",
 | |
| 	       device->name);
 | |
| 	  device = device->next;
 | |
| 	}
 | |
|     }
 | |
| 
 | |
|   /* check wether we have found a match or reach the end of the device list */
 | |
|   if (!device)
 | |
|     {
 | |
|       DBG (DBG_info, "sane_open: no device found\n");
 | |
|       return SANE_STATUS_INVAL;
 | |
|     }
 | |
| 
 | |
|   /* now we have a device, duplicate it and return it in handle */
 | |
|   DBG (DBG_info, "sane_open: device %s found\n", name);
 | |
| 
 | |
|   /* device initialization */
 | |
|   if (device->initialized == SANE_FALSE)
 | |
|     {
 | |
|       /**
 | |
|        * call to hardware initialization function here.
 | |
|        */
 | |
|       device->fd = open_pp (device->name);
 | |
|       if (device->fd < 0)
 | |
| 	{
 | |
| 	  DBG (DBG_error, "sane_open: failed to open '%s' device!\n",
 | |
| 	       device->name);
 | |
| 	  return SANE_STATUS_INVAL;
 | |
| 	}
 | |
| 
 | |
|       /* now try to connect to scanner */
 | |
|       if (connect (device->fd) != SANE_TRUE)
 | |
| 	{
 | |
| 	  DBG (DBG_error, "sane_open: failed to connect!\n");
 | |
| 	  close_pp (device->fd);
 | |
| 	  return SANE_STATUS_INVAL;
 | |
| 	}
 | |
| 
 | |
|       /* load calibration data */
 | |
|       restore_calibration (device);
 | |
| 
 | |
|       /* device link is OK now */
 | |
|       device->initialized = SANE_TRUE;
 | |
|     }
 | |
|   device->buffer = NULL;
 | |
|   device->gain = NULL;
 | |
|   device->offset = NULL;
 | |
| 
 | |
|   /* prepare handle to return */
 | |
|   session = (P5_Session *) malloc (sizeof (P5_Session));
 | |
|   if (session == NULL)
 | |
|     {
 | |
|       DBG (DBG_proc, "sane_open: exit OOM\n");
 | |
|       return SANE_STATUS_NO_MEM;
 | |
|     }
 | |
| 
 | |
|   /* initalize session */
 | |
|   session->dev = device;
 | |
|   session->scanning = SANE_FALSE;
 | |
|   session->non_blocking = SANE_FALSE;
 | |
| 
 | |
|   /* initialize SANE options for this session */
 | |
|   init_options (session);
 | |
| 
 | |
|   /* add the handle to the linked list of sessions */
 | |
|   session->next = sessions;
 | |
|   sessions = session;
 | |
| 
 | |
|   /* store result */
 | |
|   *handle = session;
 | |
| 
 | |
|   /* exit success */
 | |
|   DBG (DBG_proc, "sane_open: exit\n");
 | |
|   return SANE_STATUS_GOOD;
 | |
| }
 | |
| 
 | |
| 
 | |
| /**
 | |
|  * Set non blocking mode. In this mode, read return immediatly when
 | |
|  * no data is available whithin sane_read(), instead of polling the scanner.
 | |
|  */
 | |
| SANE_Status
 | |
| sane_set_io_mode (SANE_Handle handle, SANE_Bool non_blocking)
 | |
| {
 | |
|   P5_Session *session = (P5_Session *) handle;
 | |
| 
 | |
|   DBG (DBG_proc, "sane_set_io_mode: start\n");
 | |
|   if (session->scanning != SANE_TRUE)
 | |
|     {
 | |
|       DBG (DBG_error, "sane_set_io_mode: called out of a scan\n");
 | |
|       return SANE_STATUS_INVAL;
 | |
|     }
 | |
|   session->non_blocking = non_blocking;
 | |
|   DBG (DBG_info, "sane_set_io_mode: I/O mode set to %sblocking.\n",
 | |
|        non_blocking ? "non " : " ");
 | |
|   DBG (DBG_proc, "sane_set_io_mode: exit\n");
 | |
|   return SANE_STATUS_GOOD;
 | |
| }
 | |
| 
 | |
| 
 | |
| /**
 | |
|  * An advanced method we don't support but have to define. At SANE API
 | |
|  * level this function is meant to provide a file descriptor on which the
 | |
|  * frontend can do select()/poll() to wait for data.
 | |
|  */
 | |
| SANE_Status
 | |
| sane_get_select_fd (SANE_Handle handle, SANE_Int * fdp)
 | |
| {
 | |
|   /* make compiler happy ... */
 | |
|   handle = handle;
 | |
|   fdp = fdp;
 | |
| 
 | |
|   DBG (DBG_proc, "sane_get_select_fd: start\n");
 | |
|   DBG (DBG_warn, "sane_get_select_fd: unsupported ...\n");
 | |
|   DBG (DBG_proc, "sane_get_select_fd: exit\n");
 | |
|   return SANE_STATUS_UNSUPPORTED;
 | |
| }
 | |
| 
 | |
| 
 | |
| /**
 | |
|  * Returns the options we know.
 | |
|  *
 | |
|  * From the SANE spec:
 | |
|  * This function is used to access option descriptors. The function
 | |
|  * returns the option descriptor for option number n of the device
 | |
|  * represented by handle h. Option number 0 is guaranteed to be a
 | |
|  * valid option. Its value is an integer that specifies the number of
 | |
|  * options that are available for device handle h (the count includes
 | |
|  * option 0). If n is not a valid option index, the function returns
 | |
|  * NULL. The returned option descriptor is guaranteed to remain valid
 | |
|  * (and at the returned address) until the device is closed.
 | |
|  */
 | |
| const SANE_Option_Descriptor *
 | |
| sane_get_option_descriptor (SANE_Handle handle, SANE_Int option)
 | |
| {
 | |
|   struct P5_Session *session = handle;
 | |
| 
 | |
|   DBG (DBG_proc, "sane_get_option_descriptor: start\n");
 | |
| 
 | |
|   if ((unsigned) option >= NUM_OPTIONS)
 | |
|     return NULL;
 | |
| 
 | |
|   DBG (DBG_info, "sane_get_option_descriptor: \"%s\"\n",
 | |
|        session->options[option].descriptor.name);
 | |
| 
 | |
|   DBG (DBG_proc, "sane_get_option_descriptor: exit\n");
 | |
|   return &(session->options[option].descriptor);
 | |
| }
 | |
| 
 | |
| /**
 | |
|  * sets automatic value for an option , called by sane_control_option after
 | |
|  * all checks have been done */
 | |
| static SANE_Status
 | |
| set_automatic_value (P5_Session * s, int option, SANE_Int * myinfo)
 | |
| {
 | |
|   SANE_Status status = SANE_STATUS_GOOD;
 | |
|   SANE_Int i, min;
 | |
|   SANE_Word *dpi_list;
 | |
| 
 | |
|   switch (option)
 | |
|     {
 | |
|     case OPT_TL_X:
 | |
|       s->options[OPT_TL_X].value.w = x_range.min;
 | |
|       *myinfo |= SANE_INFO_RELOAD_PARAMS;
 | |
|       break;
 | |
|     case OPT_TL_Y:
 | |
|       s->options[OPT_TL_Y].value.w = y_range.min;
 | |
|       *myinfo |= SANE_INFO_RELOAD_PARAMS;
 | |
|       break;
 | |
|     case OPT_BR_X:
 | |
|       s->options[OPT_BR_X].value.w = x_range.max;
 | |
|       *myinfo |= SANE_INFO_RELOAD_PARAMS;
 | |
|       break;
 | |
|     case OPT_BR_Y:
 | |
|       s->options[OPT_BR_Y].value.w = y_range.max;
 | |
|       *myinfo |= SANE_INFO_RELOAD_PARAMS;
 | |
|       break;
 | |
|     case OPT_RESOLUTION:
 | |
|       /* we set up to the lowest available dpi value */
 | |
|       dpi_list =
 | |
| 	(SANE_Word *) s->options[OPT_RESOLUTION].descriptor.constraint.
 | |
| 	word_list;
 | |
|       min = 65536;
 | |
|       for (i = 1; i < dpi_list[0]; i++)
 | |
| 	{
 | |
| 	  if (dpi_list[i] < min)
 | |
| 	    min = dpi_list[i];
 | |
| 	}
 | |
|       s->options[OPT_RESOLUTION].value.w = min;
 | |
|       *myinfo |= SANE_INFO_RELOAD_PARAMS;
 | |
|       break;
 | |
|     case OPT_PREVIEW:
 | |
|       s->options[OPT_PREVIEW].value.w = SANE_FALSE;
 | |
|       *myinfo |= SANE_INFO_RELOAD_PARAMS;
 | |
|       break;
 | |
|     case OPT_MODE:
 | |
|       if (s->options[OPT_MODE].value.s)
 | |
| 	free (s->options[OPT_MODE].value.s);
 | |
|       s->options[OPT_MODE].value.s = strdup (mode_list[0]);
 | |
|       *myinfo |= SANE_INFO_RELOAD_OPTIONS;
 | |
|       *myinfo |= SANE_INFO_RELOAD_PARAMS;
 | |
|       break;
 | |
|     default:
 | |
|       DBG (DBG_warn, "set_automatic_value: can't set unknown option %d\n",
 | |
| 	   option);
 | |
|     }
 | |
| 
 | |
|   return status;
 | |
| }
 | |
| 
 | |
| /**
 | |
|  * sets an option , called by sane_control_option after all
 | |
|  * checks have been done */
 | |
| static SANE_Status
 | |
| set_option_value (P5_Session * s, int option, void *val, SANE_Int * myinfo)
 | |
| {
 | |
|   SANE_Status status = SANE_STATUS_GOOD;
 | |
|   SANE_Word tmpw;
 | |
| 
 | |
|   switch (option)
 | |
|     {
 | |
|     case OPT_TL_X:
 | |
|     case OPT_BR_X:
 | |
|     case OPT_TL_Y:
 | |
|     case OPT_BR_Y:
 | |
|       s->options[option].value.w = *(SANE_Word *) val;
 | |
|       /* we ensure geometry is coherent */
 | |
|       /* this happens when user drags TL corner right or below the BR point */
 | |
|       if (s->options[OPT_BR_Y].value.w < s->options[OPT_TL_Y].value.w)
 | |
| 	{
 | |
| 	  tmpw = s->options[OPT_BR_Y].value.w;
 | |
| 	  s->options[OPT_BR_Y].value.w = s->options[OPT_TL_Y].value.w;
 | |
| 	  s->options[OPT_TL_Y].value.w = tmpw;
 | |
| 	}
 | |
|       if (s->options[OPT_BR_X].value.w < s->options[OPT_TL_X].value.w)
 | |
| 	{
 | |
| 	  tmpw = s->options[OPT_BR_X].value.w;
 | |
| 	  s->options[OPT_BR_X].value.w = s->options[OPT_TL_X].value.w;
 | |
| 	  s->options[OPT_TL_X].value.w = tmpw;
 | |
| 	}
 | |
| 
 | |
|       *myinfo |= SANE_INFO_RELOAD_PARAMS;
 | |
|       break;
 | |
| 
 | |
|     case OPT_RESOLUTION:
 | |
|     case OPT_PREVIEW:
 | |
|       s->options[option].value.w = *(SANE_Word *) val;
 | |
|       *myinfo |= SANE_INFO_RELOAD_PARAMS;
 | |
|       break;
 | |
| 
 | |
|     case OPT_MODE:
 | |
|       if (s->options[option].value.s)
 | |
| 	free (s->options[option].value.s);
 | |
|       s->options[option].value.s = strdup (val);
 | |
|       *myinfo |= SANE_INFO_RELOAD_PARAMS | SANE_INFO_RELOAD_OPTIONS;
 | |
|       break;
 | |
| 
 | |
|     case OPT_CALIBRATE:
 | |
|       status = sheetfed_calibration (s->dev);
 | |
|       *myinfo |= SANE_INFO_RELOAD_OPTIONS;
 | |
|       break;
 | |
| 
 | |
|     case OPT_CLEAR_CALIBRATION:
 | |
|       cleanup_calibration (s->dev);
 | |
|       *myinfo |= SANE_INFO_RELOAD_OPTIONS;
 | |
|       break;
 | |
| 
 | |
|     default:
 | |
|       DBG (DBG_warn, "set_option_value: can't set unknown option %d\n",
 | |
| 	   option);
 | |
|     }
 | |
|   return status;
 | |
| }
 | |
| 
 | |
| /**
 | |
|  * gets an option , called by sane_control_option after all checks
 | |
|  * have been done */
 | |
| static SANE_Status
 | |
| get_option_value (P5_Session * s, int option, void *val)
 | |
| {
 | |
|   SANE_Status status;
 | |
| 
 | |
|   switch (option)
 | |
|     {
 | |
|       /* word or word equivalent options: */
 | |
|     case OPT_NUM_OPTS:
 | |
|     case OPT_RESOLUTION:
 | |
|     case OPT_PREVIEW:
 | |
|     case OPT_TL_X:
 | |
|     case OPT_TL_Y:
 | |
|     case OPT_BR_X:
 | |
|     case OPT_BR_Y:
 | |
|       *(SANE_Word *) val = s->options[option].value.w;
 | |
|       break;
 | |
| 
 | |
|       /* string options: */
 | |
|     case OPT_MODE:
 | |
|       strcpy (val, s->options[option].value.s);
 | |
|       break;
 | |
| 
 | |
|       /* sensor options */
 | |
|     case OPT_PAGE_LOADED_SW:
 | |
|       status = test_document (s->dev->fd);
 | |
|       if (status == SANE_STATUS_GOOD)
 | |
| 	s->options[option].value.b = SANE_TRUE;
 | |
|       else
 | |
| 	s->options[option].value.b = SANE_FALSE;
 | |
|       *(SANE_Bool *) val = s->options[option].value.b;
 | |
|       break;
 | |
| 
 | |
|     case OPT_NEED_CALIBRATION_SW:
 | |
|       *(SANE_Bool *) val = !s->dev->calibrated;
 | |
|       break;
 | |
| 
 | |
| 
 | |
|       /* unhandled options */
 | |
|     default:
 | |
|       DBG (DBG_warn, "get_option_value: can't get unknown option %d\n",
 | |
| 	   option);
 | |
|     }
 | |
| 
 | |
|   return SANE_STATUS_GOOD;
 | |
| }
 | |
| 
 | |
| /**
 | |
|  * Gets or sets an option value.
 | |
|  *
 | |
|  * From the SANE spec:
 | |
|  * This function is used to set or inquire the current value of option
 | |
|  * number n of the device represented by handle h. The manner in which
 | |
|  * the option is controlled is specified by parameter action. The
 | |
|  * possible values of this parameter are described in more detail
 | |
|  * below.  The value of the option is passed through argument val. It
 | |
|  * is a pointer to the memory that holds the option value. The memory
 | |
|  * area pointed to by v must be big enough to hold the entire option
 | |
|  * value (determined by member size in the corresponding option
 | |
|  * descriptor).
 | |
|  *
 | |
|  * The only exception to this rule is that when setting the value of a
 | |
|  * string option, the string pointed to by argument v may be shorter
 | |
|  * since the backend will stop reading the option value upon
 | |
|  * encountering the first NUL terminator in the string. If argument i
 | |
|  * is not NULL, the value of *i will be set to provide details on how
 | |
|  * well the request has been met.
 | |
|  * action is SANE_ACTION_GET_VALUE, SANE_ACTION_SET_VALUE or SANE_ACTION_SET_AUTO
 | |
|  */
 | |
| SANE_Status
 | |
| sane_control_option (SANE_Handle handle, SANE_Int option,
 | |
| 		     SANE_Action action, void *val, SANE_Int * info)
 | |
| {
 | |
|   P5_Session *s = handle;
 | |
|   SANE_Status status;
 | |
|   SANE_Word cap;
 | |
|   SANE_Int myinfo = 0;
 | |
| 
 | |
|   DBG (DBG_io2,
 | |
|        "sane_control_option: start: action = %s, option = %s (%d)\n",
 | |
|        (action == SANE_ACTION_GET_VALUE) ? "get" : (action ==
 | |
| 						    SANE_ACTION_SET_VALUE) ?
 | |
|        "set" : (action == SANE_ACTION_SET_AUTO) ? "set_auto" : "unknown",
 | |
|        s->options[option].descriptor.name, option);
 | |
| 
 | |
|   if (info)
 | |
|     *info = 0;
 | |
| 
 | |
|   /* do checks before trying to apply action */
 | |
| 
 | |
|   if (s->scanning)
 | |
|     {
 | |
|       DBG (DBG_warn, "sane_control_option: don't call this function while "
 | |
| 	   "scanning (option = %s (%d))\n",
 | |
| 	   s->options[option].descriptor.name, option);
 | |
|       return SANE_STATUS_DEVICE_BUSY;
 | |
|     }
 | |
| 
 | |
|   /* option must be within existing range */
 | |
|   if (option >= NUM_OPTIONS || option < 0)
 | |
|     {
 | |
|       DBG (DBG_warn,
 | |
| 	   "sane_control_option: option %d >= NUM_OPTIONS || option < 0\n",
 | |
| 	   option);
 | |
|       return SANE_STATUS_INVAL;
 | |
|     }
 | |
| 
 | |
|   /* don't access an inactive option */
 | |
|   cap = s->options[option].descriptor.cap;
 | |
|   if (!SANE_OPTION_IS_ACTIVE (cap))
 | |
|     {
 | |
|       DBG (DBG_warn, "sane_control_option: option %d is inactive\n", option);
 | |
|       return SANE_STATUS_INVAL;
 | |
|     }
 | |
| 
 | |
|   /* now checks have been done, apply action */
 | |
|   switch (action)
 | |
|     {
 | |
|     case SANE_ACTION_GET_VALUE:
 | |
|       status = get_option_value (s, option, val);
 | |
|       break;
 | |
| 
 | |
|     case SANE_ACTION_SET_VALUE:
 | |
|       if (!SANE_OPTION_IS_SETTABLE (cap))
 | |
| 	{
 | |
| 	  DBG (DBG_warn, "sane_control_option: option %d is not settable\n",
 | |
| 	       option);
 | |
| 	  return SANE_STATUS_INVAL;
 | |
| 	}
 | |
| 
 | |
|       status =
 | |
| 	sanei_constrain_value (&s->options[option].descriptor, val, &myinfo);
 | |
|       if (status != SANE_STATUS_GOOD)
 | |
| 	{
 | |
| 	  DBG (DBG_warn,
 | |
| 	       "sane_control_option: sanei_constrain_value returned %s\n",
 | |
| 	       sane_strstatus (status));
 | |
| 	  return status;
 | |
| 	}
 | |
| 
 | |
|       /* return immediatly if no change */
 | |
|       if (s->options[option].descriptor.type == SANE_TYPE_INT
 | |
| 	  && *(SANE_Word *) val == s->options[option].value.w)
 | |
| 	{
 | |
| 	  status = SANE_STATUS_GOOD;
 | |
| 	}
 | |
|       else
 | |
| 	{			/* apply change */
 | |
| 	  status = set_option_value (s, option, val, &myinfo);
 | |
| 	}
 | |
|       break;
 | |
| 
 | |
|     case SANE_ACTION_SET_AUTO:
 | |
|       /* sets automatic values */
 | |
|       if (!(cap & SANE_CAP_AUTOMATIC))
 | |
| 	{
 | |
| 	  DBG (DBG_warn,
 | |
| 	       "sane_control_option: option %d is not autosettable\n",
 | |
| 	       option);
 | |
| 	  return SANE_STATUS_INVAL;
 | |
| 	}
 | |
| 
 | |
|       status = set_automatic_value (s, option, &myinfo);
 | |
|       break;
 | |
| 
 | |
|     default:
 | |
|       DBG (DBG_error, "sane_control_option: invalid action %d\n", action);
 | |
|       status = SANE_STATUS_INVAL;
 | |
|       break;
 | |
|     }
 | |
| 
 | |
|   if (info)
 | |
|     *info = myinfo;
 | |
| 
 | |
|   DBG (DBG_io2, "sane_control_option: exit\n");
 | |
|   return status;
 | |
| }
 | |
| 
 | |
| /**
 | |
|  * Called by SANE when a page acquisition operation is to be started.
 | |
|  * @param handle opaque handle to a frontend session
 | |
|  * @return SANE_STATUS_GOOD on success, SANE_STATUS_BUSY if the device is
 | |
|  * in use by another session or SANE_STATUS_WARMING_UP if the device is
 | |
|  * warming up. In this case the fronted as to call sane_start again until
 | |
|  * warming up is done. Any other values returned are error status.
 | |
|  */
 | |
| SANE_Status
 | |
| sane_start (SANE_Handle handle)
 | |
| {
 | |
|   struct P5_Session *session = handle;
 | |
|   int status = SANE_STATUS_GOOD;
 | |
|   P5_Device *dev = session->dev;
 | |
| 
 | |
|   DBG (DBG_proc, "sane_start: start\n");
 | |
| 
 | |
|   /* if already scanning, tell we're busy */
 | |
|   if (session->scanning == SANE_TRUE)
 | |
|     {
 | |
|       DBG (DBG_info, "sane_start: device is already scanning\n");
 | |
|       return SANE_STATUS_DEVICE_BUSY;
 | |
|     }
 | |
| 
 | |
|   /* check that the device has been initialized */
 | |
|   if (dev->initialized == SANE_FALSE)
 | |
|     {
 | |
|       DBG (DBG_error, "sane_start: device is not initialized\n");
 | |
|       return SANE_STATUS_INVAL;
 | |
|     }
 | |
| 
 | |
|   /* check if there is a document */
 | |
|   status = test_document (dev->fd);
 | |
|   if (status != SANE_STATUS_GOOD)
 | |
|     {
 | |
|       DBG (DBG_error, "sane_start: device is already scanning\n");
 | |
|       return status;
 | |
|     }
 | |
| 
 | |
|   /* we compute all the scan parameters so that */
 | |
|   /* we will be able to set up the registers correctly */
 | |
|   compute_parameters (session);
 | |
| 
 | |
|   /* move to scan area if needed */
 | |
|   if (dev->ystart > 0)
 | |
|     {
 | |
|       status = move (dev);
 | |
|       if (status != SANE_STATUS_GOOD)
 | |
| 	{
 | |
| 	  DBG (DBG_error, "sane_start: failed to move to scan area\n");
 | |
| 	  return SANE_STATUS_INVAL;
 | |
| 	}
 | |
|     }
 | |
| 
 | |
|   /* send scan command */
 | |
|   status = start_scan (dev, dev->mode, dev->ydpi, dev->xstart, dev->pixels);
 | |
|   if (status != SANE_STATUS_GOOD)
 | |
|     {
 | |
|       DBG (DBG_error, "sane_start: failed to start scan\n");
 | |
|       return SANE_STATUS_INVAL;
 | |
|     }
 | |
| 
 | |
|   /* allocates work buffer */
 | |
|   if (dev->buffer != NULL)
 | |
|     {
 | |
|       free (dev->buffer);
 | |
|     }
 | |
| 
 | |
|   dev->position = 0;
 | |
|   dev->top = 0;
 | |
|   /* compute amount of lines needed for lds correction */
 | |
|   dev->bottom = dev->bytes_per_line * 2 * dev->lds;
 | |
|   /* computes buffer size, 66 color lines plus eventual amount needed for lds */
 | |
|   dev->size = dev->pixels * 3 * 66 + dev->bottom;
 | |
|   dev->buffer = (uint8_t *) malloc (dev->size);
 | |
|   if (dev->buffer == NULL)
 | |
|     {
 | |
|       DBG (DBG_error, "sane_start: failed to allocate %lu bytes\n", (unsigned long)dev->size);
 | |
|       sane_cancel (handle);
 | |
|       return SANE_STATUS_NO_MEM;
 | |
|     }
 | |
| 
 | |
|   /* return now the scan has been initiated */
 | |
|   session->scanning = SANE_TRUE;
 | |
|   session->sent = 0;
 | |
| 
 | |
|   DBG (DBG_io, "sane_start: to_send=%d\n", session->to_send);
 | |
|   DBG (DBG_io, "sane_start: size=%lu\n", (unsigned long)dev->size);
 | |
|   DBG (DBG_io, "sane_start: top=%lu\n", (unsigned long)dev->top);
 | |
|   DBG (DBG_io, "sane_start: bottom=%lu\n", (unsigned long)dev->bottom);
 | |
|   DBG (DBG_io, "sane_start: position=%lu\n", (unsigned long)dev->position);
 | |
| 
 | |
|   DBG (DBG_proc, "sane_start: exit\n");
 | |
|   return status;
 | |
| }
 | |
| 
 | |
| /** @brief compute scan parameters
 | |
|  * This function computes two set of parameters. The one for the SANE's standard
 | |
|  * and the other for the hardware. Among these parameters are the bit depth, total
 | |
|  * number of lines, total number of columns, extra line to read for data reordering...
 | |
|  * @param session fronted session to compute final scan parameters
 | |
|  * @return SANE_STATUS_GOOD on success
 | |
|  */
 | |
| static SANE_Status
 | |
| compute_parameters (P5_Session * session)
 | |
| {
 | |
|   P5_Device *dev = session->dev;
 | |
|   SANE_Int dpi;			/* dpi for scan */
 | |
|   SANE_String mode;
 | |
|   SANE_Status status = SANE_STATUS_GOOD;
 | |
| 
 | |
|   int tl_x, tl_y, br_x, br_y;
 | |
| 
 | |
|   mode = session->options[OPT_MODE].value.s;
 | |
|   dpi = session->options[OPT_RESOLUTION].value.w;
 | |
| 
 | |
|   /* scan coordinates */
 | |
|   tl_x = SANE_UNFIX (session->options[OPT_TL_X].value.w);
 | |
|   tl_y = SANE_UNFIX (session->options[OPT_TL_Y].value.w);
 | |
|   br_x = SANE_UNFIX (session->options[OPT_BR_X].value.w);
 | |
|   br_y = SANE_UNFIX (session->options[OPT_BR_Y].value.w);
 | |
| 
 | |
|   /* only single pass scanning supported */
 | |
|   session->params.last_frame = SANE_TRUE;
 | |
| 
 | |
|   /* gray modes */
 | |
|   if (strcmp (mode, GRAY_MODE) == 0)
 | |
|     {
 | |
|       session->params.format = SANE_FRAME_GRAY;
 | |
|       dev->mode = MODE_GRAY;
 | |
|       dev->lds = 0;
 | |
|     }
 | |
|   else if (strcmp (mode, LINEART_MODE) == 0)
 | |
|     {
 | |
|       session->params.format = SANE_FRAME_GRAY;
 | |
|       dev->mode = MODE_LINEART;
 | |
|       dev->lds = 0;
 | |
|     }
 | |
|   else
 | |
|     {
 | |
|       /* Color */
 | |
|       session->params.format = SANE_FRAME_RGB;
 | |
|       dev->mode = MODE_COLOR;
 | |
|       dev->lds = (dev->model->lds * dpi) / dev->model->max_ydpi;
 | |
|     }
 | |
| 
 | |
|   /* SANE level values */
 | |
|   session->params.lines = ((br_y - tl_y) * dpi) / MM_PER_INCH;
 | |
|   if (session->params.lines == 0)
 | |
|     session->params.lines = 1;
 | |
|   session->params.pixels_per_line = ((br_x - tl_x) * dpi) / MM_PER_INCH;
 | |
|   if (session->params.pixels_per_line == 0)
 | |
|     session->params.pixels_per_line = 1;
 | |
| 
 | |
|   DBG (DBG_data, "compute_parameters: pixels_per_line   =%d\n",
 | |
|        session->params.pixels_per_line);
 | |
| 
 | |
|   if (strcmp (mode, LINEART_MODE) == 0)
 | |
|     {
 | |
|       session->params.depth = 1;
 | |
|       /* in lineart, having pixels multiple of 8 avoids a costly test */
 | |
|       /* at each bit to see we must go to the next byte               */
 | |
|       /* TODO : implement this requirement in sane_control_option */
 | |
|       session->params.pixels_per_line =
 | |
| 	((session->params.pixels_per_line + 7) / 8) * 8;
 | |
|     }
 | |
|   else
 | |
|     session->params.depth = 8;
 | |
| 
 | |
|   /* width needs to be even */
 | |
|   if (session->params.pixels_per_line & 1)
 | |
|     session->params.pixels_per_line++;
 | |
| 
 | |
|   /* Hardware settings : they can differ from the ones at SANE level */
 | |
|   /* for instance the effective DPI used by a sensor may be higher   */
 | |
|   /* than the one needed for the SANE scan parameters                */
 | |
|   dev->lines = session->params.lines;
 | |
|   dev->pixels = session->params.pixels_per_line;
 | |
| 
 | |
|   /* motor and sensor DPI */
 | |
|   dev->xdpi = dpi;
 | |
|   dev->ydpi = dpi;
 | |
| 
 | |
|   /* handle bounds of motor's dpi range */
 | |
|   if (dev->ydpi > dev->model->max_ydpi)
 | |
|     {
 | |
|       dev->ydpi = dev->model->max_ydpi;
 | |
|       dev->lines = (dev->lines * dev->model->max_ydpi) / dpi;
 | |
|       if (dev->lines == 0)
 | |
| 	dev->lines = 1;
 | |
| 
 | |
|       /* round number of lines */
 | |
|       session->params.lines =
 | |
| 	(session->params.lines / dev->lines) * dev->lines;
 | |
|       if (session->params.lines == 0)
 | |
| 	session->params.lines = 1;
 | |
|     }
 | |
|   if (dev->ydpi < dev->model->min_ydpi)
 | |
|     {
 | |
|       dev->ydpi = dev->model->min_ydpi;
 | |
|       dev->lines = (dev->lines * dev->model->min_ydpi) / dpi;
 | |
|     }
 | |
| 
 | |
|   /* hardware values */
 | |
|   dev->xstart =
 | |
|     ((SANE_UNFIX (dev->model->x_offset) + tl_x) * dpi) / MM_PER_INCH;
 | |
|   dev->ystart =
 | |
|     ((SANE_UNFIX (dev->model->y_offset) + tl_y) * dev->ydpi) / MM_PER_INCH;
 | |
| 
 | |
|   /* take lds correction into account when moving to scan area */
 | |
|   if (dev->ystart > 2 * dev->lds)
 | |
|     dev->ystart -= 2 * dev->lds;
 | |
| 
 | |
| 
 | |
|   /* computes bytes per line */
 | |
|   session->params.bytes_per_line = session->params.pixels_per_line;
 | |
|   dev->bytes_per_line = dev->pixels;
 | |
|   if (session->params.format == SANE_FRAME_RGB)
 | |
|     {
 | |
|       dev->bytes_per_line *= 3;
 | |
|     }
 | |
| 
 | |
|   /* in lineart mode we adjust bytes_per_line needed by frontend */
 | |
|   /* we do that here because we needed sent/to_send to be as if  */
 | |
|   /* there was no lineart                                        */
 | |
|   if (session->params.depth == 1)
 | |
|     {
 | |
|       session->params.bytes_per_line =
 | |
| 	(session->params.bytes_per_line + 7) / 8;
 | |
|     }
 | |
| 
 | |
|   session->params.bytes_per_line = dev->bytes_per_line;
 | |
|   session->to_send = session->params.bytes_per_line * session->params.lines;
 | |
|   session->params.bytes_per_line = dev->bytes_per_line;
 | |
| 
 | |
|   DBG (DBG_data, "compute_parameters: bytes_per_line    =%d\n",
 | |
|        session->params.bytes_per_line);
 | |
|   DBG (DBG_data, "compute_parameters: depth             =%d\n",
 | |
|        session->params.depth);
 | |
|   DBG (DBG_data, "compute_parameters: lines             =%d\n",
 | |
|        session->params.lines);
 | |
|   DBG (DBG_data, "compute_parameters: image size        =%d\n",
 | |
|        session->to_send);
 | |
| 
 | |
|   DBG (DBG_data, "compute_parameters: xstart            =%d\n", dev->xstart);
 | |
|   DBG (DBG_data, "compute_parameters: ystart            =%d\n", dev->ystart);
 | |
|   DBG (DBG_data, "compute_parameters: dev lines         =%d\n", dev->lines);
 | |
|   DBG (DBG_data, "compute_parameters: dev bytes per line=%d\n",
 | |
|        dev->bytes_per_line);
 | |
|   DBG (DBG_data, "compute_parameters: dev pixels        =%d\n", dev->pixels);
 | |
|   DBG (DBG_data, "compute_parameters: lds               =%d\n", dev->lds);
 | |
| 
 | |
|   return status;
 | |
| }
 | |
| 
 | |
| 
 | |
| /**
 | |
|  * Called by SANE to retrieve information about the type of data
 | |
|  * that the current scan will return.
 | |
|  *
 | |
|  * From the SANE spec:
 | |
|  * This function is used to obtain the current scan parameters. The
 | |
|  * returned parameters are guaranteed to be accurate between the time
 | |
|  * a scan has been started (sane_start() has been called) and the
 | |
|  * completion of that request. Outside of that window, the returned
 | |
|  * values are best-effort estimates of what the parameters will be
 | |
|  * when sane_start() gets invoked.
 | |
|  *
 | |
|  * Calling this function before a scan has actually started allows,
 | |
|  * for example, to get an estimate of how big the scanned image will
 | |
|  * be. The parameters passed to this function are the handle of the
 | |
|  * device for which the parameters should be obtained and a pointer
 | |
|  * to a parameter structure.
 | |
|  */
 | |
| SANE_Status
 | |
| sane_get_parameters (SANE_Handle handle, SANE_Parameters * params)
 | |
| {
 | |
|   SANE_Status status;
 | |
|   struct P5_Session *session = (struct P5_Session *) handle;
 | |
| 
 | |
|   DBG (DBG_proc, "sane_get_parameters: start\n");
 | |
| 
 | |
|   /* call parameters computing function */
 | |
|   status = compute_parameters (session);
 | |
|   if (status == SANE_STATUS_GOOD && params)
 | |
|     *params = session->params;
 | |
| 
 | |
|   DBG (DBG_proc, "sane_get_parameters: exit\n");
 | |
|   return status;
 | |
| }
 | |
| 
 | |
| 
 | |
| /**
 | |
|  * Called by SANE to read data.
 | |
|  *
 | |
|  * From the SANE spec:
 | |
|  * This function is used to read image data from the device
 | |
|  * represented by handle h.  Argument buf is a pointer to a memory
 | |
|  * area that is at least maxlen bytes long.  The number of bytes
 | |
|  * returned is stored in *len. A backend must set this to zero when
 | |
|  * the call fails (i.e., when a status other than SANE_STATUS_GOOD is
 | |
|  * returned).
 | |
|  *
 | |
|  * When the call succeeds, the number of bytes returned can be
 | |
|  * anywhere in the range from 0 to maxlen bytes.
 | |
|  *
 | |
|  * Returned data is read from working buffer.
 | |
|  */
 | |
| SANE_Status
 | |
| sane_read (SANE_Handle handle, SANE_Byte * buf,
 | |
| 	   SANE_Int max_len, SANE_Int * len)
 | |
| {
 | |
|   struct P5_Session *session = (struct P5_Session *) handle;
 | |
|   struct P5_Device *dev = session->dev;
 | |
|   SANE_Status status = SANE_STATUS_GOOD;
 | |
|   int count;
 | |
|   int size, lines;
 | |
|   SANE_Bool x2;
 | |
|   SANE_Int i;
 | |
| 
 | |
|   DBG (DBG_proc, "sane_read: start\n");
 | |
|   DBG (DBG_io, "sane_read: up to %d bytes required by frontend\n", max_len);
 | |
| 
 | |
|   /* some sanity checks first to protect from would be buggy frontends */
 | |
|   if (!session)
 | |
|     {
 | |
|       DBG (DBG_error, "sane_read: handle is null!\n");
 | |
|       return SANE_STATUS_INVAL;
 | |
|     }
 | |
| 
 | |
|   if (!buf)
 | |
|     {
 | |
|       DBG (DBG_error, "sane_read: buf is null!\n");
 | |
|       return SANE_STATUS_INVAL;
 | |
|     }
 | |
| 
 | |
|   if (!len)
 | |
|     {
 | |
|       DBG (DBG_error, "sane_read: len is null!\n");
 | |
|       return SANE_STATUS_INVAL;
 | |
|     }
 | |
| 
 | |
|   /* no data read yet */
 | |
|   *len = 0;
 | |
| 
 | |
|   /* check if session is scanning */
 | |
|   if (!session->scanning)
 | |
|     {
 | |
|       DBG (DBG_warn,
 | |
| 	   "sane_read: scan was cancelled, is over or has not been initiated yet\n");
 | |
|       return SANE_STATUS_CANCELLED;
 | |
|     }
 | |
| 
 | |
|   /* check for EOF, must be done before any physical read */
 | |
|   if (session->sent >= session->to_send)
 | |
|     {
 | |
|       DBG (DBG_io, "sane_read: end of scan reached\n");
 | |
|       return SANE_STATUS_EOF;
 | |
|     }
 | |
| 
 | |
|   /* if working buffer is empty, we do a physical data read */
 | |
|   if (dev->top <= dev->bottom)
 | |
|     {
 | |
|       DBG (DBG_io, "sane_read: physical data read\n");
 | |
|       /* check is there is data available. In case of non-blocking mode we return
 | |
|        * as soon it is detected there is no data yet. Reads must by done line by
 | |
|        * line, so we read only when count is bigger than bytes per line
 | |
|        * */
 | |
|       count = available_bytes (dev->fd);
 | |
|       DBG (DBG_io, "sane_read: count=%d bytes\n", count);
 | |
|       if (count < dev->bytes_per_line && session->non_blocking == SANE_TRUE)
 | |
| 	{
 | |
| 	  DBG (DBG_io, "sane_read: scanner hasn't enough data available\n");
 | |
| 	  DBG (DBG_proc, "sane_read: exit\n");
 | |
| 	  return SANE_STATUS_GOOD;
 | |
| 	}
 | |
| 
 | |
|       /* now we can wait for data here */
 | |
|       while (count < dev->bytes_per_line)
 | |
| 	{
 | |
| 	  /* test if document left the feeder, so we have to terminate the scan */
 | |
| 	  status = test_document (dev->fd);
 | |
| 	  if (status == SANE_STATUS_NO_DOCS)
 | |
| 	    {
 | |
| 	      session->to_send = session->sent;
 | |
| 	      return SANE_STATUS_EOF;
 | |
| 	    }
 | |
| 
 | |
| 	  /* don't call scanner too often */
 | |
| 	  usleep (10000);
 | |
| 	  count = available_bytes (dev->fd);
 | |
| 	}
 | |
| 
 | |
|       /** compute size of physical data to read
 | |
|        * on first read, position will be 0, while it will be 'bottom'
 | |
|        * for the subsequent reads.
 | |
|        * We try to read a complete buffer */
 | |
|       size = dev->size - dev->position;
 | |
| 
 | |
|       if (session->to_send - session->sent < size)
 | |
| 	{
 | |
| 	  /* not enough data left, so read remainder of scan */
 | |
| 	  size = session->to_send - session->sent;
 | |
| 	}
 | |
| 
 | |
|       /* 600 dpi is 300x600 physical, and 400 is 200x400 */
 | |
|       if (dev->ydpi > dev->model->max_xdpi)
 | |
| 	{
 | |
| 	  x2 = SANE_TRUE;
 | |
| 	}
 | |
|       else
 | |
| 	{
 | |
| 	  x2 = SANE_FALSE;
 | |
| 	}
 | |
|       lines = read_line (dev,
 | |
| 			 dev->buffer + dev->position,
 | |
| 			 dev->bytes_per_line,
 | |
| 			 size / dev->bytes_per_line,
 | |
| 			 SANE_TRUE, x2, dev->mode, dev->calibrated);
 | |
| 
 | |
|       /* handle document end detection TODO try to recover the partial
 | |
|        * buffer already read before EOD */
 | |
|       if (lines == -1)
 | |
| 	{
 | |
| 	  DBG (DBG_io, "sane_read: error reading line\n");
 | |
| 	  return SANE_STATUS_IO_ERROR;
 | |
| 	}
 | |
| 
 | |
|       /* gather lines until we have more than needed for lds */
 | |
|       dev->position += lines * dev->bytes_per_line;
 | |
|       dev->top = dev->position;
 | |
|       if (dev->position > dev->bottom)
 | |
| 	{
 | |
| 	  dev->position = dev->bottom;
 | |
| 	}
 | |
|       DBG (DBG_io, "sane_read: size    =%lu\n", (unsigned long)dev->size);
 | |
|       DBG (DBG_io, "sane_read: bottom  =%lu\n", (unsigned long)dev->bottom);
 | |
|       DBG (DBG_io, "sane_read: position=%lu\n", (unsigned long)dev->position);
 | |
|       DBG (DBG_io, "sane_read: top     =%lu\n", (unsigned long)dev->top);
 | |
|     }				/* end of physical data reading */
 | |
| 
 | |
|   /* logical data reading */
 | |
|   /* check if there data available in working buffer */
 | |
|   if (dev->position < dev->top && dev->position >= dev->bottom)
 | |
|     {
 | |
|       DBG (DBG_io, "sane_read: logical data read\n");
 | |
|       /* we have more data in internal buffer than asked ,
 | |
|        * then send only max data */
 | |
|       size = dev->top - dev->position;
 | |
|       if (max_len < size)
 | |
| 	{
 | |
| 	  *len = max_len;
 | |
| 	}
 | |
|       else
 | |
| 	/* if we don't have enough, send all what we have */
 | |
| 	{
 | |
| 	  *len = dev->top - dev->position;
 | |
| 	}
 | |
| 
 | |
|       /* data copy */
 | |
|       if (dev->lds == 0)
 | |
| 	{
 | |
| 	  memcpy (buf, dev->buffer + dev->position, *len);
 | |
| 	}
 | |
|       else
 | |
| 	{
 | |
| 	  /* compute count of bytes for lds */
 | |
| 	  count = dev->lds * dev->bytes_per_line;
 | |
| 
 | |
| 	  /* adjust for lds as we copy data to frontend */
 | |
| 	  for (i = 0; i < *len; i++)
 | |
| 	    {
 | |
| 	      switch ((dev->position + i) % 3)
 | |
| 		{
 | |
| 		  /* red */
 | |
| 		case 0:
 | |
| 		  buf[i] = dev->buffer[dev->position + i - 2 * count];
 | |
| 		  break;
 | |
| 		  /* green */
 | |
| 		case 1:
 | |
| 		  buf[i] = dev->buffer[dev->position + i - count];
 | |
| 		  break;
 | |
| 		  /* blue */
 | |
| 		default:
 | |
| 		  buf[i] = dev->buffer[dev->position + i];
 | |
| 		  break;
 | |
| 		}
 | |
| 	    }
 | |
| 	}
 | |
|       dev->position += *len;
 | |
| 
 | |
|       /* update byte accounting */
 | |
|       session->sent += *len;
 | |
|       DBG (DBG_io, "sane_read: sent %d bytes from buffer to frontend\n",
 | |
| 	   *len);
 | |
|       return SANE_STATUS_GOOD;
 | |
|     }
 | |
| 
 | |
|   /* check if we exhausted working buffer */
 | |
|   if (dev->position >= dev->top && dev->position >= dev->bottom)
 | |
|     {
 | |
|       /* copy extra lines needed for lds in next buffer */
 | |
|       if (dev->position > dev->bottom && dev->lds > 0)
 | |
| 	{
 | |
| 	  memcpy (dev->buffer,
 | |
| 		  dev->buffer + dev->position - dev->bottom, dev->bottom);
 | |
| 	}
 | |
| 
 | |
|       /* restart buffer */
 | |
|       dev->position = dev->bottom;
 | |
|       dev->top = 0;
 | |
|     }
 | |
| 
 | |
|   DBG (DBG_io, "sane_read: size    =%lu\n", (unsigned long)dev->size);
 | |
|   DBG (DBG_io, "sane_read: bottom  =%lu\n", (unsigned long)dev->bottom);
 | |
|   DBG (DBG_io, "sane_read: position=%lu\n", (unsigned long)dev->position);
 | |
|   DBG (DBG_io, "sane_read: top     =%lu\n", (unsigned long)dev->top);
 | |
| 
 | |
|   DBG (DBG_proc, "sane_read: exit\n");
 | |
|   return status;
 | |
| }
 | |
| 
 | |
| 
 | |
| /**
 | |
|  * Cancels a scan.
 | |
|  *
 | |
|  * From the SANE spec:
 | |
|  * This function is used to immediately or as quickly as possible
 | |
|  * cancel the currently pending operation of the device represented by
 | |
|  * handle h.  This function can be called at any time (as long as
 | |
|  * handle h is a valid handle) but usually affects long-running
 | |
|  * operations only (such as image is acquisition). It is safe to call
 | |
|  * this function asynchronously (e.g., from within a signal handler).
 | |
|  * It is important to note that completion of this operaton does not
 | |
|  * imply that the currently pending operation has been cancelled. It
 | |
|  * only guarantees that cancellation has been initiated. Cancellation
 | |
|  * completes only when the cancelled call returns (typically with a
 | |
|  * status value of SANE_STATUS_CANCELLED).  Since the SANE API does
 | |
|  * not require any other operations to be re-entrant, this implies
 | |
|  * that a frontend must not call any other operation until the
 | |
|  * cancelled operation has returned.
 | |
|  */
 | |
| void
 | |
| sane_cancel (SANE_Handle handle)
 | |
| {
 | |
|   P5_Session *session = handle;
 | |
| 
 | |
|   DBG (DBG_proc, "sane_cancel: start\n");
 | |
| 
 | |
|   /* if scanning, abort and park head */
 | |
|   if (session->scanning == SANE_TRUE)
 | |
|     {
 | |
|       /* detects if we are called after the scan is finished,
 | |
|        * or if the scan is aborted */
 | |
|       if (session->sent < session->to_send)
 | |
| 	{
 | |
| 	  DBG (DBG_info, "sane_cancel: aborting scan.\n");
 | |
| 	  /* device hasn't finished scan, we are aborting it
 | |
| 	   * and we may have to do something specific for it here */
 | |
| 	}
 | |
|       else
 | |
| 	{
 | |
| 	  DBG (DBG_info, "sane_cancel: cleaning up after scan.\n");
 | |
| 	}
 | |
|       session->scanning = SANE_FALSE;
 | |
|     }
 | |
|   eject (session->dev->fd);
 | |
| 
 | |
|   DBG (DBG_proc, "sane_cancel: exit\n");
 | |
| }
 | |
| 
 | |
| 
 | |
| /**
 | |
|  * Ends use of the session.
 | |
|  *
 | |
|  * From the SANE spec:
 | |
|  * This function terminates the association between the device handle
 | |
|  * passed in argument h and the device it represents. If the device is
 | |
|  * presently active, a call to sane_cancel() is performed first. After
 | |
|  * this function returns, handle h must not be used anymore.
 | |
|  *
 | |
|  * Handle resources are free'd before disposing the handle. But devices
 | |
|  * resources must not be mdofied, since it could be used or reused until
 | |
|  * sane_exit() is called.
 | |
|  */
 | |
| void
 | |
| sane_close (SANE_Handle handle)
 | |
| {
 | |
|   P5_Session *prev, *session;
 | |
| 
 | |
|   DBG (DBG_proc, "sane_close: start\n");
 | |
| 
 | |
|   /* remove handle from list of open handles: */
 | |
|   prev = NULL;
 | |
|   for (session = sessions; session; session = session->next)
 | |
|     {
 | |
|       if (session == handle)
 | |
| 	break;
 | |
|       prev = session;
 | |
|     }
 | |
|   if (!session)
 | |
|     {
 | |
|       DBG (DBG_error0, "close: invalid handle %p\n", handle);
 | |
|       return;			/* oops, not a handle we know about */
 | |
|     }
 | |
| 
 | |
|   /* cancel any active scan */
 | |
|   if (session->scanning == SANE_TRUE)
 | |
|     {
 | |
|       sane_cancel (handle);
 | |
|     }
 | |
| 
 | |
|   if (prev)
 | |
|     prev->next = session->next;
 | |
|   else
 | |
|     sessions = session->next;
 | |
| 
 | |
|   /* close low level device */
 | |
|   if (session->dev->initialized == SANE_TRUE)
 | |
|     {
 | |
|       if (session->dev->calibrated == SANE_TRUE)
 | |
| 	{
 | |
| 	  save_calibration (session->dev);
 | |
| 	}
 | |
|       disconnect (session->dev->fd);
 | |
|       close_pp (session->dev->fd);
 | |
|       session->dev->fd = -1;
 | |
|       session->dev->initialized = SANE_FALSE;
 | |
| 
 | |
|       /* free device data */
 | |
|       if (session->dev->buffer != NULL)
 | |
| 	{
 | |
| 	  free (session->dev->buffer);
 | |
| 	}
 | |
|       if (session->dev->buffer != NULL)
 | |
| 	{
 | |
| 	  free (session->dev->gain);
 | |
| 	  free (session->dev->offset);
 | |
| 	}
 | |
|       if (session->dev->calibrated == SANE_TRUE)
 | |
| 	{
 | |
| 	  cleanup_calibration (session->dev);
 | |
| 	}
 | |
|     }
 | |
| 
 | |
|   /* free per session data */
 | |
|   free (session->options[OPT_MODE].value.s);
 | |
|   free ((void *)session->options[OPT_RESOLUTION].descriptor.constraint.word_list);
 | |
| 
 | |
|   free (session);
 | |
| 
 | |
|   DBG (DBG_proc, "sane_close: exit\n");
 | |
| }
 | |
| 
 | |
| 
 | |
| /**
 | |
|  * Terminates the backend.
 | |
|  *
 | |
|  * From the SANE spec:
 | |
|  * This function must be called to terminate use of a backend. The
 | |
|  * function will first close all device handles that still might be
 | |
|  * open (it is recommended to close device handles explicitly through
 | |
|  * a call to sane_close(), but backends are required to release all
 | |
|  * resources upon a call to this function). After this function
 | |
|  * returns, no function other than sane_init() may be called
 | |
|  * (regardless of the status value returned by sane_exit(). Neglecting
 | |
|  * to call this function may result in some resources not being
 | |
|  * released properly.
 | |
|  */
 | |
| void
 | |
| sane_exit (void)
 | |
| {
 | |
|   struct P5_Session *session, *next;
 | |
|   struct P5_Device *dev, *nextdev;
 | |
|   int i;
 | |
| 
 | |
|   DBG (DBG_proc, "sane_exit: start\n");
 | |
|   init_count--;
 | |
| 
 | |
|   if (init_count > 0)
 | |
|     {
 | |
|       DBG (DBG_info,
 | |
| 	   "sane_exit: still %d fronteds to leave before effective exit.\n",
 | |
| 	   init_count);
 | |
|       return;
 | |
|     }
 | |
| 
 | |
|   /* free session structs */
 | |
|   for (session = sessions; session; session = next)
 | |
|     {
 | |
|       next = session->next;
 | |
|       sane_close ((SANE_Handle *) session);
 | |
|       free (session);
 | |
|     }
 | |
|   sessions = NULL;
 | |
| 
 | |
|   /* free devices structs */
 | |
|   for (dev = devices; dev; dev = nextdev)
 | |
|     {
 | |
|       nextdev = dev->next;
 | |
|       free (dev->name);
 | |
|       free (dev);
 | |
|     }
 | |
|   devices = NULL;
 | |
| 
 | |
|   /* now list of devices */
 | |
|   if (devlist)
 | |
|     {
 | |
|       i = 0;
 | |
|       while ((SANE_Device *) devlist[i])
 | |
| 	{
 | |
| 	  free ((SANE_Device *) devlist[i]);
 | |
| 	  i++;
 | |
| 	}
 | |
|       free (devlist);
 | |
|       devlist = NULL;
 | |
|     }
 | |
| 
 | |
|   DBG (DBG_proc, "sane_exit: exit\n");
 | |
| }
 | |
| 
 | |
| 
 | |
| /** @brief probe for all supported devices
 | |
|  * This functions tries to probe if any of the supported devices of
 | |
|  * the backend is present. Each detected device will be added to the
 | |
|  * 'devices' list
 | |
|  */
 | |
| static SANE_Status
 | |
| probe_p5_devices (void)
 | |
| {
 | |
|   /**> configuration structure used during attach */
 | |
|   SANEI_Config config;
 | |
|   /**> list of configuration options */
 | |
|   SANE_Option_Descriptor *cfg_options[NUM_CFG_OPTIONS];
 | |
|   /**> placeholders pointers for option values */
 | |
|   void *values[NUM_CFG_OPTIONS];
 | |
|   int i;
 | |
|   SANE_Status status;
 | |
| 
 | |
|   DBG (DBG_proc, "probe_p5_devices: start\n");
 | |
| 
 | |
|   /* initialize configuration options */
 | |
|   cfg_options[CFG_MODEL_NAME] =
 | |
|     (SANE_Option_Descriptor *) malloc (sizeof (SANE_Option_Descriptor));
 | |
|   cfg_options[CFG_MODEL_NAME]->name = "modelname";
 | |
|   cfg_options[CFG_MODEL_NAME]->desc = "user provided scanner's model name";
 | |
|   cfg_options[CFG_MODEL_NAME]->type = SANE_TYPE_INT;
 | |
|   cfg_options[CFG_MODEL_NAME]->unit = SANE_UNIT_NONE;
 | |
|   cfg_options[CFG_MODEL_NAME]->size = sizeof (SANE_Word);
 | |
|   cfg_options[CFG_MODEL_NAME]->cap = SANE_CAP_SOFT_SELECT;
 | |
|   cfg_options[CFG_MODEL_NAME]->constraint_type = SANE_CONSTRAINT_NONE;
 | |
|   values[CFG_MODEL_NAME] = &p5cfg.modelname;
 | |
| 
 | |
|   /* set configuration options structure */
 | |
|   config.descriptors = cfg_options;
 | |
|   config.values = values;
 | |
|   config.count = NUM_CFG_OPTIONS;
 | |
| 
 | |
|   /* generic configure and attach function */
 | |
|   status = sanei_configure_attach (P5_CONFIG_FILE, &config, config_attach);
 | |
|   /* free allocated options */
 | |
|   for (i = 0; i < NUM_CFG_OPTIONS; i++)
 | |
|     {
 | |
|       free (cfg_options[i]);
 | |
|     }
 | |
| 
 | |
|   DBG (DBG_proc, "probe_p5_devices: end\n");
 | |
|   return status;
 | |
| }
 | |
| 
 | |
| /** This function is called by sanei_configure_attach to try
 | |
|  * to attach the backend to a device specified by the configuration file.
 | |
|  *
 | |
|  * @param config configuration structure filled with values read
 | |
|  * 	         from configuration file
 | |
|  * @param devname name of the device to try to attach to, it is
 | |
|  * 	          the unprocessed line of the configuration file
 | |
|  *
 | |
|  * @return status SANE_STATUS_GOOD if no errors (even if no matching
 | |
|  * 	    devices found)
 | |
|  * 	   SANE_STATUS_INVAL in case of error
 | |
|  */
 | |
| static SANE_Status
 | |
| config_attach (SANEI_Config * config, const char *devname)
 | |
| {
 | |
|   /* currently, the config is a global variable so config is useless here */
 | |
|   /* the correct thing would be to have a generic sanei_attach_matching_devices
 | |
|    * using an attach function with a config parameter */
 | |
|   config = config;
 | |
| 
 | |
|   /* the devname has been processed and is ready to be used
 | |
|    * directly. The config struct contains all the configuration data for
 | |
|    * the corresponding device. Since there is no ressources common to each
 | |
|    * backends regarding parallel port, we can directly call the attach
 | |
|    * function. */
 | |
|   attach_p5 (devname, config);
 | |
| 
 | |
|   return SANE_STATUS_GOOD;
 | |
| }
 | |
| 
 | |
| /** @brief try to attach to a device by its name
 | |
|  * The attach tries to open the given device and match it
 | |
|  * with devices handled by the backend. The configuration parameter
 | |
|  * contains the values of the already parsed configuration options
 | |
|  * from the conf file.
 | |
|  * @param config configuration structure filled with values read
 | |
|  * 	         from configuration file
 | |
|  * @param devicename name of the device to try to attach to, it is
 | |
|  * 	          the unprocessed line of the configuration file
 | |
|  *
 | |
|  * @return status SANE_STATUS_GOOD if no errors (even if no matching
 | |
|  * 	    devices found)
 | |
|  * 	   SANE_STATUS_NOM_MEM if there isn't enough memory to allocate the
 | |
|  * 	   			device structure
 | |
|  * 	   SANE_STATUS_UNSUPPORTED if the device if unknown by the backend
 | |
|  * 	   SANE_STATUS_INVAL in case of other error
 | |
|  */
 | |
| static SANE_Status
 | |
| attach_p5 (const char *devicename, SANEI_Config * config)
 | |
| {
 | |
|   struct P5_Device *device;
 | |
|   struct P5_Model *model;
 | |
| 
 | |
|   DBG (DBG_proc, "attach(%s): start\n", devicename);
 | |
|   if(config==NULL)
 | |
|     {
 | |
|       DBG (DBG_warn, "attach: config is NULL\n");
 | |
|     }
 | |
| 
 | |
|   /* search if we already have it attached */
 | |
|   for (device = devices; device; device = device->next)
 | |
|     {
 | |
|       if (strcmp (device->name, devicename) == 0)
 | |
| 	{
 | |
| 	  DBG (DBG_info, "attach: device already attached\n");
 | |
| 	  DBG (DBG_proc, "attach: exit\n");
 | |
| 	  return SANE_STATUS_GOOD;
 | |
| 	}
 | |
|     }
 | |
| 
 | |
|   /**
 | |
|    * do physical probe of the device here. In case the device is recognized,
 | |
|    * we allocate a device struct and give it options and model.
 | |
|    * Else we return SANE_STATUS_UNSUPPORTED.
 | |
|    */
 | |
|   model = probe (devicename);
 | |
|   if (model == NULL)
 | |
|     {
 | |
|       DBG (DBG_info,
 | |
| 	   "attach: device %s is not managed by the backend\n", devicename);
 | |
|       DBG (DBG_proc, "attach: exit\n");
 | |
|       return SANE_STATUS_UNSUPPORTED;
 | |
|     }
 | |
| 
 | |
|   /* allocate device struct */
 | |
|   device = malloc (sizeof (*device));
 | |
|   if (device == NULL)
 | |
|     {
 | |
|       return SANE_STATUS_NO_MEM;
 | |
|       DBG (DBG_proc, "attach: exit\n");
 | |
|     }
 | |
|   memset (device, 0, sizeof (*device));
 | |
|   device->model = model;
 | |
| 
 | |
|   /* name of the device */
 | |
|   device->name = strdup (devicename);
 | |
| 
 | |
|   DBG (DBG_info, "attach: found %s %s %s at %s\n",
 | |
|        device->model->vendor, device->model->product, device->model->type,
 | |
|        device->name);
 | |
| 
 | |
|   /* we insert new device at start of the chained list */
 | |
|   /* head of the list becomes the next, and start is replaced */
 | |
|   /* with the new session struct */
 | |
|   device->next = devices;
 | |
|   devices = device;
 | |
| 
 | |
|   /* intialization is done at sane_open */
 | |
|   device->initialized = SANE_FALSE;
 | |
|   device->calibrated = SANE_FALSE;
 | |
| 
 | |
|   DBG (DBG_proc, "attach: exit\n");
 | |
|   return SANE_STATUS_GOOD;
 | |
| }
 | |
| 
 | |
| 
 | |
| /** @brief set initial value for the scanning options
 | |
|  * for each sessions, control options are initalized based on the capability
 | |
|  * of the model of the physical device.
 | |
|  * @param session scanner session to initialize options
 | |
|  * @return SANE_STATUS_GOOD on success
 | |
|  */
 | |
| static SANE_Status
 | |
| init_options (struct P5_Session *session)
 | |
| {
 | |
|   SANE_Int option, i, min, idx;
 | |
|   SANE_Word *dpi_list;
 | |
|   P5_Model *model = session->dev->model;
 | |
| 
 | |
|   DBG (DBG_proc, "init_options: start\n");
 | |
| 
 | |
|   /* we first initialize each options with a default value */
 | |
|   memset (session->options, 0, sizeof (session->options[OPT_NUM_OPTS]));
 | |
|   for (option = 0; option < NUM_OPTIONS; option++)
 | |
|     {
 | |
|       session->options[option].descriptor.size = sizeof (SANE_Word);
 | |
|       session->options[option].descriptor.cap =
 | |
| 	SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT;
 | |
|     }
 | |
| 
 | |
|   /* we set up all the options listed in the P5_Option enum */
 | |
| 
 | |
|   /* last option / end of list marker */
 | |
|   session->options[OPT_NUM_OPTS].descriptor.name = SANE_NAME_NUM_OPTIONS;
 | |
|   session->options[OPT_NUM_OPTS].descriptor.title = SANE_TITLE_NUM_OPTIONS;
 | |
|   session->options[OPT_NUM_OPTS].descriptor.desc = SANE_DESC_NUM_OPTIONS;
 | |
|   session->options[OPT_NUM_OPTS].descriptor.type = SANE_TYPE_INT;
 | |
|   session->options[OPT_NUM_OPTS].descriptor.cap = SANE_CAP_SOFT_DETECT;
 | |
|   session->options[OPT_NUM_OPTS].value.w = NUM_OPTIONS;
 | |
| 
 | |
|   /* "Standard" group: */
 | |
|   session->options[OPT_STANDARD_GROUP].descriptor.title = SANE_TITLE_STANDARD;
 | |
|   session->options[OPT_STANDARD_GROUP].descriptor.name = SANE_NAME_STANDARD;
 | |
|   session->options[OPT_STANDARD_GROUP].descriptor.desc = SANE_DESC_STANDARD;
 | |
|   session->options[OPT_STANDARD_GROUP].descriptor.type = SANE_TYPE_GROUP;
 | |
|   session->options[OPT_STANDARD_GROUP].descriptor.size = 0;
 | |
|   session->options[OPT_STANDARD_GROUP].descriptor.cap = 0;
 | |
|   session->options[OPT_STANDARD_GROUP].descriptor.constraint_type =
 | |
|     SANE_CONSTRAINT_NONE;
 | |
| 
 | |
|   /* scan mode */
 | |
|   session->options[OPT_MODE].descriptor.name = SANE_NAME_SCAN_MODE;
 | |
|   session->options[OPT_MODE].descriptor.title = SANE_TITLE_SCAN_MODE;
 | |
|   session->options[OPT_MODE].descriptor.desc = SANE_DESC_SCAN_MODE;
 | |
|   session->options[OPT_MODE].descriptor.type = SANE_TYPE_STRING;
 | |
|   session->options[OPT_MODE].descriptor.cap |= SANE_CAP_AUTOMATIC;
 | |
|   session->options[OPT_MODE].descriptor.constraint_type =
 | |
|     SANE_CONSTRAINT_STRING_LIST;
 | |
|   session->options[OPT_MODE].descriptor.size = max_string_size (mode_list);
 | |
|   session->options[OPT_MODE].descriptor.constraint.string_list = mode_list;
 | |
|   session->options[OPT_MODE].value.s = strdup (mode_list[0]);
 | |
| 
 | |
|   /* preview */
 | |
|   session->options[OPT_PREVIEW].descriptor.name = SANE_NAME_PREVIEW;
 | |
|   session->options[OPT_PREVIEW].descriptor.title = SANE_TITLE_PREVIEW;
 | |
|   session->options[OPT_PREVIEW].descriptor.desc = SANE_DESC_PREVIEW;
 | |
|   session->options[OPT_PREVIEW].descriptor.type = SANE_TYPE_BOOL;
 | |
|   session->options[OPT_PREVIEW].descriptor.cap |= SANE_CAP_AUTOMATIC;
 | |
|   session->options[OPT_PREVIEW].descriptor.unit = SANE_UNIT_NONE;
 | |
|   session->options[OPT_PREVIEW].descriptor.constraint_type =
 | |
|     SANE_CONSTRAINT_NONE;
 | |
|   session->options[OPT_PREVIEW].value.w = SANE_FALSE;
 | |
| 
 | |
|   /** @brief build resolution list
 | |
|    * We merge xdpi and ydpi list to provide only one resolution option control.
 | |
|    * This is the most common case for backends and fronteds and give 'square'
 | |
|    * pixels. The SANE API allow to control x and y dpi independantly, but this is
 | |
|    * rarely done and may confuse both frontends and users. In case a dpi value exists
 | |
|    * for one but not for the other, the backend will have to crop data so that the
 | |
|    * frontend is unaffected. A common case is that motor resolution (ydpi) is higher
 | |
|    * than sensor resolution (xdpi), so scan lines must be scaled up to keep square
 | |
|    * pixel when doing sane_read().
 | |
|    * TODO this deserves a dedicated function and some unit testing
 | |
|    */
 | |
| 
 | |
|   /* find minimum first */
 | |
|   min = 65535;
 | |
|   for (i = 0; i < MAX_RESOLUTIONS && model->xdpi_values[i] > 0; i++)
 | |
|     {
 | |
|       if (model->xdpi_values[i] < min)
 | |
| 	min = model->xdpi_values[i];
 | |
|     }
 | |
|   for (i = 0; i < MAX_RESOLUTIONS && model->ydpi_values[i] > 0; i++)
 | |
|     {
 | |
|       if (model->ydpi_values[i] < min)
 | |
| 	min = model->ydpi_values[i];
 | |
|     }
 | |
| 
 | |
|   dpi_list = malloc ((MAX_RESOLUTIONS * 2 + 1) * sizeof (SANE_Word));
 | |
|   if (!dpi_list)
 | |
|     return SANE_STATUS_NO_MEM;
 | |
|   dpi_list[1] = min;
 | |
|   idx = 2;
 | |
| 
 | |
|   /* find any value greater than the last used min and
 | |
|    * less than the max value
 | |
|    */
 | |
|   do
 | |
|     {
 | |
|       min = 65535;
 | |
|       for (i = 0; i < MAX_RESOLUTIONS && model->xdpi_values[i] > 0; i++)
 | |
| 	{
 | |
| 	  if (model->xdpi_values[i] < min
 | |
| 	      && model->xdpi_values[i] > dpi_list[idx - 1])
 | |
| 	    min = model->xdpi_values[i];
 | |
| 	}
 | |
|       for (i = 0; i < MAX_RESOLUTIONS && model->ydpi_values[i] > 0; i++)
 | |
| 	{
 | |
| 	  if (model->ydpi_values[i] < min
 | |
| 	      && model->ydpi_values[i] > dpi_list[idx - 1])
 | |
| 	    min = model->ydpi_values[i];
 | |
| 	}
 | |
|       if (min < 65535)
 | |
| 	{
 | |
| 	  dpi_list[idx] = min;
 | |
| 	  idx++;
 | |
| 	}
 | |
|     }
 | |
|   while (min != 65535);
 | |
|   dpi_list[idx] = 0;
 | |
|   /* the count of different resolution is put at the beginning */
 | |
|   dpi_list[0] = idx - 1;
 | |
| 
 | |
|   session->options[OPT_RESOLUTION].descriptor.name =
 | |
|     SANE_NAME_SCAN_RESOLUTION;
 | |
|   session->options[OPT_RESOLUTION].descriptor.title =
 | |
|     SANE_TITLE_SCAN_RESOLUTION;
 | |
|   session->options[OPT_RESOLUTION].descriptor.desc =
 | |
|     SANE_DESC_SCAN_RESOLUTION;
 | |
|   session->options[OPT_RESOLUTION].descriptor.type = SANE_TYPE_INT;
 | |
|   session->options[OPT_RESOLUTION].descriptor.cap |= SANE_CAP_AUTOMATIC;
 | |
|   session->options[OPT_RESOLUTION].descriptor.unit = SANE_UNIT_DPI;
 | |
|   session->options[OPT_RESOLUTION].descriptor.constraint_type =
 | |
|     SANE_CONSTRAINT_WORD_LIST;
 | |
|   session->options[OPT_RESOLUTION].descriptor.constraint.word_list = dpi_list;
 | |
| 
 | |
|   /* initial value is lowest available dpi */
 | |
|   session->options[OPT_RESOLUTION].value.w = min;
 | |
| 
 | |
|   /* "Geometry" group: */
 | |
|   session->options[OPT_GEOMETRY_GROUP].descriptor.title = SANE_TITLE_GEOMETRY;
 | |
|   session->options[OPT_GEOMETRY_GROUP].descriptor.name = SANE_NAME_GEOMETRY;
 | |
|   session->options[OPT_GEOMETRY_GROUP].descriptor.desc = SANE_DESC_GEOMETRY;
 | |
|   session->options[OPT_GEOMETRY_GROUP].descriptor.type = SANE_TYPE_GROUP;
 | |
|   session->options[OPT_GEOMETRY_GROUP].descriptor.cap = SANE_CAP_ADVANCED;
 | |
|   session->options[OPT_GEOMETRY_GROUP].descriptor.size = 0;
 | |
|   session->options[OPT_GEOMETRY_GROUP].descriptor.constraint_type =
 | |
|     SANE_CONSTRAINT_NONE;
 | |
| 
 | |
|   /* adapt the constraint range to the detected model */
 | |
|   x_range.max = model->x_size;
 | |
|   y_range.max = model->y_size;
 | |
| 
 | |
|   /* top-left x */
 | |
|   session->options[OPT_TL_X].descriptor.name = SANE_NAME_SCAN_TL_X;
 | |
|   session->options[OPT_TL_X].descriptor.title = SANE_TITLE_SCAN_TL_X;
 | |
|   session->options[OPT_TL_X].descriptor.desc = SANE_DESC_SCAN_TL_X;
 | |
|   session->options[OPT_TL_X].descriptor.type = SANE_TYPE_FIXED;
 | |
|   session->options[OPT_TL_X].descriptor.cap |= SANE_CAP_AUTOMATIC;
 | |
|   session->options[OPT_TL_X].descriptor.unit = SANE_UNIT_MM;
 | |
|   session->options[OPT_TL_X].descriptor.constraint_type =
 | |
|     SANE_CONSTRAINT_RANGE;
 | |
|   session->options[OPT_TL_X].descriptor.constraint.range = &x_range;
 | |
|   session->options[OPT_TL_X].value.w = 0;
 | |
| 
 | |
|   /* top-left y */
 | |
|   session->options[OPT_TL_Y].descriptor.name = SANE_NAME_SCAN_TL_Y;
 | |
|   session->options[OPT_TL_Y].descriptor.title = SANE_TITLE_SCAN_TL_Y;
 | |
|   session->options[OPT_TL_Y].descriptor.desc = SANE_DESC_SCAN_TL_Y;
 | |
|   session->options[OPT_TL_Y].descriptor.type = SANE_TYPE_FIXED;
 | |
|   session->options[OPT_TL_Y].descriptor.cap |= SANE_CAP_AUTOMATIC;
 | |
|   session->options[OPT_TL_Y].descriptor.unit = SANE_UNIT_MM;
 | |
|   session->options[OPT_TL_Y].descriptor.constraint_type =
 | |
|     SANE_CONSTRAINT_RANGE;
 | |
|   session->options[OPT_TL_Y].descriptor.constraint.range = &y_range;
 | |
|   session->options[OPT_TL_Y].value.w = 0;
 | |
| 
 | |
|   /* bottom-right x */
 | |
|   session->options[OPT_BR_X].descriptor.name = SANE_NAME_SCAN_BR_X;
 | |
|   session->options[OPT_BR_X].descriptor.title = SANE_TITLE_SCAN_BR_X;
 | |
|   session->options[OPT_BR_X].descriptor.desc = SANE_DESC_SCAN_BR_X;
 | |
|   session->options[OPT_BR_X].descriptor.type = SANE_TYPE_FIXED;
 | |
|   session->options[OPT_BR_X].descriptor.cap |= SANE_CAP_AUTOMATIC;
 | |
|   session->options[OPT_BR_X].descriptor.unit = SANE_UNIT_MM;
 | |
|   session->options[OPT_BR_X].descriptor.constraint_type =
 | |
|     SANE_CONSTRAINT_RANGE;
 | |
|   session->options[OPT_BR_X].descriptor.constraint.range = &x_range;
 | |
|   session->options[OPT_BR_X].value.w = x_range.max;
 | |
| 
 | |
|   /* bottom-right y */
 | |
|   session->options[OPT_BR_Y].descriptor.name = SANE_NAME_SCAN_BR_Y;
 | |
|   session->options[OPT_BR_Y].descriptor.title = SANE_TITLE_SCAN_BR_Y;
 | |
|   session->options[OPT_BR_Y].descriptor.desc = SANE_DESC_SCAN_BR_Y;
 | |
|   session->options[OPT_BR_Y].descriptor.type = SANE_TYPE_FIXED;
 | |
|   session->options[OPT_BR_Y].descriptor.cap |= SANE_CAP_AUTOMATIC;
 | |
|   session->options[OPT_BR_Y].descriptor.unit = SANE_UNIT_MM;
 | |
|   session->options[OPT_BR_Y].descriptor.constraint_type =
 | |
|     SANE_CONSTRAINT_RANGE;
 | |
|   session->options[OPT_BR_Y].descriptor.constraint.range = &y_range;
 | |
|   session->options[OPT_BR_Y].value.w = y_range.max;
 | |
| 
 | |
|   /* sensor group */
 | |
|   session->options[OPT_SENSOR_GROUP].descriptor.name = SANE_NAME_SENSORS;
 | |
|   session->options[OPT_SENSOR_GROUP].descriptor.title = SANE_TITLE_SENSORS;
 | |
|   session->options[OPT_SENSOR_GROUP].descriptor.desc = SANE_DESC_SENSORS;
 | |
|   session->options[OPT_SENSOR_GROUP].descriptor.type = SANE_TYPE_GROUP;
 | |
|   session->options[OPT_SENSOR_GROUP].descriptor.constraint_type =
 | |
|     SANE_CONSTRAINT_NONE;
 | |
| 
 | |
|   /* page loaded sensor */
 | |
|   session->options[OPT_PAGE_LOADED_SW].descriptor.name =
 | |
|     SANE_NAME_PAGE_LOADED;
 | |
|   session->options[OPT_PAGE_LOADED_SW].descriptor.title =
 | |
|     SANE_TITLE_PAGE_LOADED;
 | |
|   session->options[OPT_PAGE_LOADED_SW].descriptor.desc =
 | |
|     SANE_DESC_PAGE_LOADED;
 | |
|   session->options[OPT_PAGE_LOADED_SW].descriptor.type = SANE_TYPE_BOOL;
 | |
|   session->options[OPT_PAGE_LOADED_SW].descriptor.unit = SANE_UNIT_NONE;
 | |
|   session->options[OPT_PAGE_LOADED_SW].descriptor.cap =
 | |
|     SANE_CAP_SOFT_DETECT | SANE_CAP_HARD_SELECT | SANE_CAP_ADVANCED;
 | |
|   session->options[OPT_PAGE_LOADED_SW].value.b = 0;
 | |
| 
 | |
|   /* calibration needed */
 | |
|   session->options[OPT_NEED_CALIBRATION_SW].descriptor.name =
 | |
|     "need-calibration";
 | |
|   session->options[OPT_NEED_CALIBRATION_SW].descriptor.title =
 | |
|     SANE_I18N ("Need calibration");
 | |
|   session->options[OPT_NEED_CALIBRATION_SW].descriptor.desc =
 | |
|     SANE_I18N ("The scanner needs calibration for the current settings");
 | |
|   session->options[OPT_NEED_CALIBRATION_SW].descriptor.type = SANE_TYPE_BOOL;
 | |
|   session->options[OPT_NEED_CALIBRATION_SW].descriptor.unit = SANE_UNIT_NONE;
 | |
|   session->options[OPT_NEED_CALIBRATION_SW].descriptor.cap =
 | |
|     SANE_CAP_SOFT_DETECT | SANE_CAP_HARD_SELECT | SANE_CAP_ADVANCED;
 | |
|   session->options[OPT_NEED_CALIBRATION_SW].value.b = 0;
 | |
| 
 | |
|   /* button group */
 | |
|   session->options[OPT_BUTTON_GROUP].descriptor.name = "Buttons";
 | |
|   session->options[OPT_BUTTON_GROUP].descriptor.title = SANE_I18N ("Buttons");
 | |
|   session->options[OPT_BUTTON_GROUP].descriptor.desc = SANE_I18N ("Buttons");
 | |
|   session->options[OPT_BUTTON_GROUP].descriptor.type = SANE_TYPE_GROUP;
 | |
|   session->options[OPT_BUTTON_GROUP].descriptor.constraint_type =
 | |
|     SANE_CONSTRAINT_NONE;
 | |
| 
 | |
|   /* calibrate button */
 | |
|   session->options[OPT_CALIBRATE].descriptor.name = "calibrate";
 | |
|   session->options[OPT_CALIBRATE].descriptor.title = SANE_I18N ("Calibrate");
 | |
|   session->options[OPT_CALIBRATE].descriptor.desc =
 | |
|     SANE_I18N ("Start calibration using special sheet");
 | |
|   session->options[OPT_CALIBRATE].descriptor.type = SANE_TYPE_BUTTON;
 | |
|   session->options[OPT_CALIBRATE].descriptor.unit = SANE_UNIT_NONE;
 | |
|   session->options[OPT_CALIBRATE].descriptor.cap =
 | |
|     SANE_CAP_SOFT_DETECT | SANE_CAP_SOFT_SELECT | SANE_CAP_ADVANCED |
 | |
|     SANE_CAP_AUTOMATIC;
 | |
|   session->options[OPT_CALIBRATE].value.b = 0;
 | |
| 
 | |
|   /* clear calibration cache button */
 | |
|   session->options[OPT_CLEAR_CALIBRATION].descriptor.name = "clear";
 | |
|   session->options[OPT_CLEAR_CALIBRATION].descriptor.title =
 | |
|     SANE_I18N ("Clear calibration");
 | |
|   session->options[OPT_CLEAR_CALIBRATION].descriptor.desc =
 | |
|     SANE_I18N ("Clear calibration cache");
 | |
|   session->options[OPT_CLEAR_CALIBRATION].descriptor.type = SANE_TYPE_BUTTON;
 | |
|   session->options[OPT_CLEAR_CALIBRATION].descriptor.unit = SANE_UNIT_NONE;
 | |
|   session->options[OPT_CLEAR_CALIBRATION].descriptor.cap =
 | |
|     SANE_CAP_SOFT_DETECT | SANE_CAP_SOFT_SELECT | SANE_CAP_ADVANCED |
 | |
|     SANE_CAP_AUTOMATIC;
 | |
|   session->options[OPT_CLEAR_CALIBRATION].value.b = 0;
 | |
| 
 | |
|   /* until work on calibration isfinished */
 | |
|   DISABLE (OPT_CALIBRATE);
 | |
|   DISABLE (OPT_CLEAR_CALIBRATION);
 | |
| 
 | |
|   DBG (DBG_proc, "init_options: exit\n");
 | |
|   return SANE_STATUS_GOOD;
 | |
| }
 | |
| 
 | |
| /** @brief physical probe of a device
 | |
|  * This function probes for a scanning device using the given name. If the
 | |
|  * device is managed, a model structure describing the device will be returned.
 | |
|  * @param devicename low level device to access to probe hardware
 | |
|  * @return NULL is the device is unsupported, or a model struct describing the
 | |
|  * device.
 | |
|  */
 | |
| P5_Model *
 | |
| probe (const char *devicename)
 | |
| {
 | |
|   int fd;
 | |
| 
 | |
|   /* open parallel port device */
 | |
|   fd = open_pp (devicename);
 | |
|   if (fd < 0)
 | |
|     {
 | |
|       DBG (DBG_error, "probe: failed to open '%s' device!\n", devicename);
 | |
|       return NULL;
 | |
|     }
 | |
| 
 | |
|   /* now try to connect to scanner */
 | |
|   if (connect (fd) != SANE_TRUE)
 | |
|     {
 | |
|       DBG (DBG_error, "probe: failed to connect!\n");
 | |
|       close_pp (fd);
 | |
|       return NULL;
 | |
|     }
 | |
| 
 | |
|   /* set up for memory test */
 | |
|   write_reg (fd, REG1, 0x00);
 | |
|   write_reg (fd, REG7, 0x00);
 | |
|   write_reg (fd, REG0, 0x00);
 | |
|   write_reg (fd, REG1, 0x00);
 | |
|   write_reg (fd, REGF, 0x80);
 | |
|   if (memtest (fd, 0x0100) != SANE_TRUE)
 | |
|     {
 | |
|       disconnect (fd);
 | |
|       close_pp (fd);
 | |
|       DBG (DBG_error, "probe: memory test failed!\n");
 | |
|       return NULL;
 | |
|     }
 | |
|   else
 | |
|     {
 | |
|       DBG (DBG_info, "memtest() OK...\n");
 | |
|     }
 | |
|   write_reg (fd, REG7, 0x00);
 | |
| 
 | |
|   /* check for document presence 0xC6: present, 0xC3 no document */
 | |
|   test_document (fd);
 | |
| 
 | |
|   /* release device nd parport for next uses */
 | |
|   disconnect (fd);
 | |
|   close_pp (fd);
 | |
| 
 | |
|   /* for there is only one supported model, so we use hardcoded values */
 | |
|   DBG (DBG_proc, "probe: exit\n");
 | |
|   return &pagepartner_model;
 | |
| }
 | |
| 
 | |
| 
 | |
| /* vim: set sw=2 cino=>2se-1sn-1s{s^-1st0(0u0 smarttab expandtab: */
 |