diff options
Diffstat (limited to 'backend/escl/escl.c')
-rw-r--r-- | backend/escl/escl.c | 777 |
1 files changed, 777 insertions, 0 deletions
diff --git a/backend/escl/escl.c b/backend/escl/escl.c new file mode 100644 index 0000000..8df6c5c --- /dev/null +++ b/backend/escl/escl.c @@ -0,0 +1,777 @@ +/* sane - Scanner Access Now Easy. + + Copyright (C) 2019 Touboul Nathane + Copyright (C) 2019 Thierry HUCHARD <thierry@ordissimo.com> + + This file is part of the SANE package. + + SANE 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 3 of the License, or (at your + option) any later version. + + SANE 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 sane; see the file COPYING. If not, write to the Free + Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + + This file implements a SANE backend for eSCL scanners. */ + +#include "escl.h" + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +#include <setjmp.h> + +#include <curl/curl.h> + +#include "../include/sane/saneopts.h" +#include "../include/sane/sanei.h" +#include "../include/sane/sanei_backend.h" +#include "../include/sane/sanei_config.h" + +#define min(A,B) (((A)<(B)) ? (A) : (B)) +#define max(A,B) (((A)>(B)) ? (A) : (B)) +#define INPUT_BUFFER_SIZE 4096 + +static const SANE_Device **devlist = NULL; +static ESCL_Device *list_devices_primary = NULL; +static int num_devices = 0; + +typedef struct Handled { + struct Handled *next; + SANE_String_Const name; + char *result; + ESCL_ScanParam param; + SANE_Option_Descriptor opt[NUM_OPTIONS]; + Option_Value val[NUM_OPTIONS]; + capabilities_t *scanner; + SANE_Range x_range; + SANE_Range y_range; + SANE_Bool cancel; + SANE_Bool write_scan_data; + SANE_Bool decompress_scan_data; + SANE_Bool end_read; + SANE_Parameters ps; +} escl_sane_t; + +/** + * \fn static SANE_Status escl_add_in_list(ESCL_Device *current) + * \brief Function that adds all the element needed to my list : + * the port number, the model name, the ip address, and the type of url (http/https). + * Moreover, this function counts the number of devices found. + * + * \return SANE_STATUS_GOOD if everything is OK. + */ +static SANE_Status +escl_add_in_list(ESCL_Device *current) +{ + ++num_devices; + current->next = list_devices_primary; + list_devices_primary = current; + return (SANE_STATUS_GOOD); +} + +/** + * \fn SANE_Status escl_device_add(int port_nb, const char *model_name, char *ip_address, char *type) + * \brief Function that browses my list ('for' loop) and returns the "escl_add_in_list" function to + * adds all the element needed to my list : + * the port number, the model name, the ip address and the type of the url (http / https). + * + * \return escl_add_in_list(current) + */ +SANE_Status +escl_device_add(int port_nb, const char *model_name, char *ip_address, char *type) +{ + ESCL_Device *current = NULL; + DBG (10, "escl_device_add\n"); + for (current = list_devices_primary; current; current = current->next) { + if (strcmp(current->ip_address, ip_address) == 0 && current->port_nb == port_nb + && strcmp(current->type, type) == 0) + return (SANE_STATUS_GOOD); + } + current = malloc(sizeof(*current)); + if (current == NULL) + return (SANE_STATUS_NO_MEM); + memset(current, 0, sizeof(*current)); + current->port_nb = port_nb; + current->model_name = strdup(model_name); + current->ip_address = strdup(ip_address); + current->type = strdup(type); + return escl_add_in_list(current); +} + +/** + * \fn static inline size_t max_string_size(const SANE_String_Const strings[]) + * \brief Function that browses the string ('for' loop) and counts the number of character in the string. + * --> this allows to know the maximum size of the string. + * + * \return max_size + 1 (the size max) + */ +static inline size_t +max_string_size(const SANE_String_Const strings[]) +{ + size_t max_size = 0; + int i = 0; + + for (i = 0; strings[i]; ++i) { + size_t size = strlen (strings[i]); + if (size > max_size) + max_size = size; + } + return (max_size + 1); +} + +/** + * \fn static SANE_Device *convertFromESCLDev(ESCL_Device *cdev) + * \brief Function that checks if the url of the received scanner is secured or not (http / https). + * --> if the url is not secured, our own url will be composed like "http://'ip':'port'". + * --> else, our own url will be composed like "https://'ip':'port'". + * AND, it's in this function that we gather all the informations of the url (that were in our list) : + * the model_name, the port, the ip, and the type of url. + * SO, leaving this function, we have in memory the complete url. + * + * \return sdev (structure that contains the elements of the url) + */ +static SANE_Device * +convertFromESCLDev(ESCL_Device *cdev) +{ + SANE_Device *sdev = (SANE_Device*) calloc(1, sizeof(SANE_Device)); + char tmp[PATH_MAX] = { 0 }; + + if (strcmp(cdev->type, "_uscan._tcp") == 0 || strcmp(cdev->type, "http") == 0) + snprintf(tmp, sizeof(tmp), "http://%s:%d", cdev->ip_address, cdev->port_nb); + else + snprintf(tmp, sizeof(tmp), "https://%s:%d", cdev->ip_address, cdev->port_nb); + DBG( 1, "Escl add device : %s\n", tmp); + sdev->name = strdup(tmp); + sdev->model = strdup(cdev->model_name); + sdev->vendor = strdup("ESCL"); + sdev->type = strdup("flatbed scanner"); + return (sdev); +} + +/** + * \fn SANE_Status sane_init(SANE_Int *version_code, SANE_Auth_Callback authorize) + * \brief Function that's called before any other SANE function ; it's the first SANE function called. + * --> this function checks the SANE config. and can check the authentication of the user if + * 'authorize' value is more than SANE_TRUE. + * In this case, it will be necessary to define an authentication method. + * + * \return SANE_STATUS_GOOD (everything is OK) + */ +SANE_Status +sane_init(SANE_Int *version_code, SANE_Auth_Callback __sane_unused__ authorize) +{ + DBG_INIT(); + DBG (10, "escl sane_init\n"); + SANE_Status status = SANE_STATUS_GOOD; + curl_global_init(CURL_GLOBAL_ALL); + if (version_code != NULL) + *version_code = SANE_VERSION_CODE(1, 0, 0); + if (status != SANE_STATUS_GOOD) + return (status); + return (SANE_STATUS_GOOD); +} + +/** + * \fn void sane_exit(void) + * \brief Function that must be called to terminate use of a backend. + * This function will first close all device handles that still might be open. + * --> by freeing all the elements of my list. + * After this function, no function other than 'sane_init' may be called. + */ +void +sane_exit(void) +{ + DBG (10, "escl sane_exit\n"); + ESCL_Device *next = NULL; + + while (list_devices_primary != NULL) { + next = list_devices_primary->next; + free(list_devices_primary); + list_devices_primary = next; + } + if (devlist) + free (devlist); + list_devices_primary = NULL; + devlist = NULL; + curl_global_cleanup(); +} + +/** + * \fn static SANE_Status attach_one_config(SANEI_Config *config, const char *line) + * \brief Function that implements a configuration file to the user : + * if the user can't detect some devices, he will be able to force their detection with this config' file to use them. + * Thus, this function parses the config' file to use the device of the user with the information below : + * the type of protocol (http/https), the ip, the port number, and the model name. + * + * \return escl_add_in_list(escl_device) if the parsing worked, SANE_STATUS_GOOD otherwise. + */ +static SANE_Status +attach_one_config(SANEI_Config __sane_unused__ *config, const char *line) +{ + int port = 0; + static int count = 0; + static ESCL_Device *escl_device = NULL; + + if (strncmp(line, "[device]", 8) == 0) { + count = 0; + escl_device = (ESCL_Device*)calloc(1, sizeof(ESCL_Device)); + } + if (strncmp(line, "ip", 2) == 0) { + const char *ip_space = sanei_config_skip_whitespace(line + 2); + if (escl_device != NULL && ip_space != NULL) { + count++; + escl_device->ip_address = strdup(ip_space); + } + } + if (sscanf(line, "port %i", &port) == 1 && port != 0) { + const char *port_space = sanei_config_skip_whitespace(line + 4); + if (escl_device != NULL && port_space != NULL) { + count++; + escl_device->port_nb = port; + } + } + if (strncmp(line, "model", 5) == 0) { + const char *model_space = sanei_config_skip_whitespace(line + 5); + if (escl_device != NULL && model_space != NULL) { + count++; + escl_device->model_name = strdup(model_space); + } + } + if (strncmp(line, "type", 4) == 0) { + const char *type_space = sanei_config_skip_whitespace(line + 4); + if (escl_device != NULL && type_space != NULL) { + count++; + escl_device->type = strdup(type_space); + } + } + if (count == 4) + return (escl_add_in_list(escl_device)); + return (SANE_STATUS_GOOD); +} + +/** + * \fn SANE_Status sane_get_devices(const SANE_Device ***device_list, SANE_Bool local_only) + * \brief Function that searches for connected devices and places them in our 'device_list'. ('for' loop) + * If the attribute 'local_only' is worth SANE_FALSE, we only returns the connected devices locally. + * + * \return SANE_STATUS_GOOD if devlist != NULL ; SANE_STATUS_NO_MEM otherwise. + */ +SANE_Status +sane_get_devices(const SANE_Device ***device_list, SANE_Bool local_only) +{ + if (local_only) /* eSCL is a network-only protocol */ + return (device_list ? SANE_STATUS_GOOD : SANE_STATUS_INVAL); + + DBG (10, "escl sane_get_devices\n"); + ESCL_Device *dev = NULL; + static const SANE_Device **devlist = 0; + SANE_Status status; + + if (device_list == NULL) + return (SANE_STATUS_INVAL); + status = sanei_configure_attach(ESCL_CONFIG_FILE, NULL, attach_one_config); + if (status != SANE_STATUS_GOOD) + return (status); + escl_devices(&status); + if (status != SANE_STATUS_GOOD) + return (status); + if (devlist) + free(devlist); + devlist = (const SANE_Device **) calloc (num_devices + 1, sizeof (devlist[0])); + if (devlist == NULL) + return (SANE_STATUS_NO_MEM); + int i = 0; + for (dev = list_devices_primary; i < num_devices; dev = dev->next) { + SANE_Device *s_dev = convertFromESCLDev(dev); + devlist[i] = s_dev; + i++; + } + devlist[i] = 0; + *device_list = devlist; + return (devlist) ? SANE_STATUS_GOOD : SANE_STATUS_NO_MEM; +} + +/** + * \fn static SANE_Status init_options(SANE_String_Const name, escl_sane_t *s) + * \brief Function thzt initializes all the needed options of the received scanner + * (the resolution / the color / the margins) thanks to the informations received with + * the 'escl_capabilities' function, called just before. + * + * \return status (if everything is OK, status = SANE_STATUS_GOOD) + */ +static SANE_Status +init_options(SANE_String_Const name, escl_sane_t *s) +{ + DBG (10, "escl init_options\n"); + SANE_Status status = SANE_STATUS_GOOD; + int i = 0; + + if (name == NULL) + return (SANE_STATUS_INVAL); + memset (s->opt, 0, sizeof (s->opt)); + memset (s->val, 0, sizeof (s->val)); + 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; + } + s->x_range.min = 0; + s->x_range.max = s->scanner->MaxWidth - s->scanner->MinWidth; + s->x_range.quant = 1; + s->y_range.min = 0; + s->y_range.max = s->scanner->MaxHeight - s->scanner->MinHeight; + s->y_range.quant = 1; + 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; + + s->opt[OPT_MODE_GROUP].title = SANE_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; + + s->opt[OPT_MODE].name = SANE_NAME_SCAN_MODE; + s->opt[OPT_MODE].title = SANE_TITLE_SCAN_MODE; + s->opt[OPT_MODE].desc = SANE_DESC_SCAN_MODE; + s->opt[OPT_MODE].type = SANE_TYPE_STRING; + s->opt[OPT_MODE].unit = SANE_UNIT_NONE; + s->opt[OPT_MODE].constraint_type = SANE_CONSTRAINT_STRING_LIST; + s->opt[OPT_MODE].constraint.string_list = s->scanner->ColorModes; + s->val[OPT_MODE].s = (char *)strdup(s->scanner->ColorModes[0]); + s->opt[OPT_MODE].size = max_string_size(s->scanner->ColorModes); + s->scanner->default_color = (char *)strdup(s->scanner->ColorModes[0]); + + 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_INT; + s->opt[OPT_RESOLUTION].unit = SANE_UNIT_DPI; + s->opt[OPT_RESOLUTION].constraint_type = SANE_CONSTRAINT_WORD_LIST; + s->opt[OPT_RESOLUTION].constraint.word_list = s->scanner->SupportedResolutions; + s->val[OPT_RESOLUTION].w = s->scanner->SupportedResolutions[1]; + s->scanner->default_resolution = s->scanner->SupportedResolutions[1]; + + s->opt[OPT_PREVIEW].name = SANE_NAME_PREVIEW; + s->opt[OPT_PREVIEW].title = SANE_TITLE_PREVIEW; + s->opt[OPT_PREVIEW].desc = SANE_DESC_PREVIEW; + s->opt[OPT_PREVIEW].cap = SANE_CAP_SOFT_DETECT | SANE_CAP_SOFT_SELECT; + s->opt[OPT_PREVIEW].type = SANE_TYPE_BOOL; + s->val[OPT_PREVIEW].w = SANE_FALSE; + + s->opt[OPT_GRAY_PREVIEW].name = SANE_NAME_GRAY_PREVIEW; + s->opt[OPT_GRAY_PREVIEW].title = SANE_TITLE_GRAY_PREVIEW; + s->opt[OPT_GRAY_PREVIEW].desc = SANE_DESC_GRAY_PREVIEW; + s->opt[OPT_GRAY_PREVIEW].type = SANE_TYPE_BOOL; + s->val[OPT_GRAY_PREVIEW].w = SANE_FALSE; + + s->opt[OPT_GEOMETRY_GROUP].title = SANE_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; + + 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_FIXED; + 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 = &s->x_range; + s->val[OPT_TL_X].w = s->scanner->RiskyLeftMargin; + + 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_FIXED; + 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 = &s->y_range; + s->val[OPT_TL_Y].w = s->scanner->RiskyTopMargin; + + 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_FIXED; + 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 = &s->x_range; + s->val[OPT_BR_X].w = s->scanner->MaxWidth; + + 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_FIXED; + 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 = &s->y_range; + s->val[OPT_BR_Y].w = s->scanner->MaxHeight; + return (status); +} + +/** + * \fn SANE_Status sane_open(SANE_String_Const name, SANE_Handle *h) + * \brief Function that establishes a connection with the device named by 'name', + * and returns a 'handler' using 'SANE_Handle *h', representing it. + * Thus, it's this function that calls the 'escl_status' function firstly, + * then the 'escl_capabilities' function, and, after, the 'init_options' function. + * + * \return status (if everything is OK, status = SANE_STATUS_GOOD, otherwise, SANE_STATUS_NO_MEM/SANE_STATUS_INVAL) + */ +SANE_Status +sane_open(SANE_String_Const name, SANE_Handle *h) +{ + DBG (10, "escl sane_open\n"); + SANE_Status status; + escl_sane_t *handler = NULL; + + if (name == NULL) + return (SANE_STATUS_INVAL); + status = escl_status(name); + if (status != SANE_STATUS_GOOD) + return (status); + handler = (escl_sane_t *)calloc(1, sizeof(escl_sane_t)); + if (handler == NULL) + return (SANE_STATUS_NO_MEM); + handler->name = strdup(name); + handler->scanner = escl_capabilities(name, &status); + if (status != SANE_STATUS_GOOD) + return (status); + status = init_options(name, handler); + if (status != SANE_STATUS_GOOD) + return (status); + handler->ps.depth = 8; + handler->ps.last_frame = SANE_TRUE; + handler->ps.format = SANE_FRAME_RGB; + handler->ps.pixels_per_line = handler->val[OPT_BR_X].w; + handler->ps.lines = handler->val[OPT_BR_Y].w; + handler->ps.bytes_per_line = handler->ps.pixels_per_line * 3; + status = sane_get_parameters(handler, 0); + if (status != SANE_STATUS_GOOD) + return (status); + handler->cancel = SANE_FALSE; + handler->write_scan_data = SANE_FALSE; + handler->decompress_scan_data = SANE_FALSE; + handler->end_read = SANE_FALSE; + *h = handler; + return (status); +} + +/** + * \fn void sane_cancel(SANE_Handle h) + * \brief Function that's used to, immediately or as quickly as possible, cancel the currently + * pending operation of the device represented by 'SANE_Handle h'. + * This functions calls the 'escl_scanner' functions, that resets the scan operations. + */ +void +sane_cancel(SANE_Handle h) +{ + DBG (10, "escl sane_cancel\n"); + escl_sane_t *handler = h; + if (handler->scanner->tmp) + { + fclose(handler->scanner->tmp); + handler->scanner->tmp = NULL; + } + handler->cancel = SANE_TRUE; + escl_scanner(handler->name, handler->result); +} + +/** + * \fn void sane_close(SANE_Handle h) + * \brief Function that closes the communication with the device represented by 'SANE_Handle h'. + * This function must release the resources that were allocated to the opening of 'h'. + */ +void +sane_close(SANE_Handle h) +{ + DBG (10, "escl sane_close\n"); + if (h != NULL) { + free(h); + h = NULL; + } +} + +/** + * \fn const SANE_Option_Descriptor *sane_get_option_descriptor(SANE_Handle h, SANE_Int n) + * \brief Function that retrieves a descriptor from the n number option of the scanner + * represented by 'h'. + * The descriptor remains valid until the machine is closed. + * + * \return s->opt + n + */ +const SANE_Option_Descriptor * +sane_get_option_descriptor(SANE_Handle h, SANE_Int n) +{ + DBG (10, "escl sane_get_option_descriptor\n"); + escl_sane_t *s = h; + + if ((unsigned) n >= NUM_OPTIONS || n < 0) + return (0); + return (s->opt + n); +} + +/** + * \fn SANE_Status sane_control_option(SANE_Handle h, SANE_Int n, SANE_Action a, void *v, SANE_Int *i) + * \brief Function that defines the actions to perform for the 'n' option of the machine, + * represented by 'h', if the action is 'a'. + * There are 3 types of possible actions : + * --> SANE_ACTION_GET_VALUE: 'v' must be used to provide the value of the option. + * --> SANE_ACTION_SET_VALUE: The option must take the 'v' value. + * --> SANE_ACTION_SET_AUTO: The backend or machine must affect the option with an appropriate value. + * Moreover, the parameter 'i' is used to provide additional information about the state of + * 'n' option if SANE_ACTION_SET_VALUE has been performed. + * + * \return SANE_STATUS_GOOD if everything is OK, otherwise, SANE_STATUS_NO_MEM/SANE_STATUS_INVAL + */ +SANE_Status +sane_control_option(SANE_Handle h, SANE_Int n, SANE_Action a, void *v, SANE_Int *i) +{ + DBG (10, "escl sane_control_option\n"); + escl_sane_t *handler = h; + + if (i) + *i = 0; + if (n >= NUM_OPTIONS || n < 0) + return (SANE_STATUS_INVAL); + if (a == SANE_ACTION_GET_VALUE) { + switch (n) { + case OPT_NUM_OPTS: + case OPT_RESOLUTION: + case OPT_TL_X: + case OPT_TL_Y: + case OPT_BR_X: + case OPT_BR_Y: + case OPT_PREVIEW: + case OPT_GRAY_PREVIEW: + *(SANE_Word *) v = handler->val[n].w; + break; + case OPT_MODE: + strcpy (v, handler->val[n].s); + break; + case OPT_MODE_GROUP: + default: + break; + } + return (SANE_STATUS_GOOD); + } + if (a == SANE_ACTION_SET_VALUE) { + switch (n) { + case OPT_TL_X: + case OPT_TL_Y: + case OPT_BR_X: + case OPT_BR_Y: + case OPT_PREVIEW: + case OPT_GRAY_PREVIEW: + handler->val[n].w = *(SANE_Word *) v; + if (i && handler->val[n].w != *(SANE_Word *) v) + *i |= SANE_INFO_RELOAD_PARAMS | SANE_INFO_RELOAD_OPTIONS | SANE_INFO_INEXACT; + handler->val[n].w = *(SANE_Word *) v; + break; + case OPT_RESOLUTION: + handler->val[n].w = *(SANE_Word *) v; + if (i) + *i |= SANE_INFO_RELOAD_PARAMS | SANE_INFO_RELOAD_OPTIONS | SANE_INFO_INEXACT; + break; + case OPT_MODE: + if (handler->val[n].s) + free (handler->val[n].s); + handler->val[n].s = strdup (v); + if (i) + *i |= SANE_INFO_RELOAD_PARAMS | SANE_INFO_RELOAD_OPTIONS | SANE_INFO_INEXACT; + break; + default: + break; + } + } + return (SANE_STATUS_GOOD); +} + +/** + * \fn SANE_Status sane_start(SANE_Handle h) + * \brief Function that initiates aquisition of an image from the device represented by handle 'h'. + * This function calls the "escl_newjob" function and the "escl_scan" function. + * + * \return status (if everything is OK, status = SANE_STATUS_GOOD, otherwise, SANE_STATUS_NO_MEM/SANE_STATUS_INVAL) + */ +SANE_Status +sane_start(SANE_Handle h) +{ + DBG (10, "escl sane_start\n"); + SANE_Status status = SANE_STATUS_GOOD; + escl_sane_t *handler = h; + int w = 0; + int he = 0; + int bps = 0; + + if (handler->name == NULL) + return (SANE_STATUS_INVAL); + handler->cancel = SANE_FALSE; + handler->write_scan_data = SANE_FALSE; + handler->decompress_scan_data = SANE_FALSE; + handler->end_read = SANE_FALSE; + handler->scanner->height = handler->val[OPT_BR_Y].w; + handler->scanner->width = handler->val[OPT_BR_X].w; + handler->scanner->pos_x = handler->val[OPT_TL_X].w; + handler->scanner->pos_y = handler->val[OPT_TL_Y].w; + if(handler->scanner->default_color) + free(handler->scanner->default_color); + if (handler->val[OPT_PREVIEW].w == SANE_TRUE) + { + int i = 0, val = 9999;; + if (handler->val[OPT_GRAY_PREVIEW].w == SANE_TRUE || + !strncasecmp(handler->val[OPT_MODE].s, SANE_VALUE_SCAN_MODE_GRAY, 3)) + handler->scanner->default_color = strdup("Grayscale8"); + else + handler->scanner->default_color = strdup("RGB24"); + for (i = 1; i < handler->scanner->SupportedResolutionsSize; i++) + { + if (val > handler->scanner->SupportedResolutions[i]) + val = handler->scanner->SupportedResolutions[i]; + } + handler->scanner->default_resolution = val; + } + else + { + handler->scanner->default_resolution = handler->val[OPT_RESOLUTION].w; + if (!strncasecmp(handler->val[OPT_MODE].s, SANE_VALUE_SCAN_MODE_GRAY, 3)) + handler->scanner->default_color = strdup("Grayscale8"); + else + handler->scanner->default_color = strdup("RGB24"); + } + handler->result = escl_newjob(handler->scanner, handler->name, &status); + if (status != SANE_STATUS_GOOD) + return (status); + status = escl_scan(handler->scanner, handler->name, handler->result); + if (status != SANE_STATUS_GOOD) + return (status); + if (!strcmp(handler->scanner->default_format, "image/jpeg")) + { + status = get_JPEG_data(handler->scanner, &w, &he, &bps); + } + else if (!strcmp(handler->scanner->default_format, "image/png")) + { + status = get_PNG_data(handler->scanner, &w, &he, &bps); + } + else if (!strcmp(handler->scanner->default_format, "image/tiff")) + { + status = get_TIFF_data(handler->scanner, &w, &he, &bps); + } + else + return SANE_STATUS_INVAL; + + if (status != SANE_STATUS_GOOD) + return (status); + handler->ps.depth = 8; + handler->ps.pixels_per_line = w; + handler->ps.lines = he; + handler->ps.bytes_per_line = w * bps; + handler->ps.last_frame = SANE_TRUE; + handler->ps.format = SANE_FRAME_RGB; + return (status); +} + +/** + * \fn SANE_Status sane_get_parameters(SANE_Handle h, SANE_Parameters *p) + * \brief Function that retrieves the device parameters represented by 'h' and stores them in 'p'. + * This function is normally used after "sane_start". + * It's in this function that we choose to assign the default color. (Color or Monochrome) + * + * \return status (if everything is OK, status = SANE_STATUS_GOOD, otherwise, SANE_STATUS_NO_MEM/SANE_STATUS_INVAL) + */ +SANE_Status +sane_get_parameters(SANE_Handle h, SANE_Parameters *p) +{ + DBG (10, "escl sane_get_parameters\n"); + SANE_Status status = SANE_STATUS_GOOD; + escl_sane_t *handler = h; + + if (status != SANE_STATUS_GOOD) + return (status); + if (p != NULL) { + p->depth = 8; + p->last_frame = SANE_TRUE; + p->format = SANE_FRAME_RGB; + p->pixels_per_line = handler->ps.pixels_per_line; + p->lines = handler->ps.lines; + p->bytes_per_line = handler->ps.bytes_per_line; + } + return (status); +} + + +/** + * \fn SANE_Status sane_read(SANE_Handle h, SANE_Byte *buf, SANE_Int maxlen, SANE_Int *len) + * \brief Function that's used to read image data from the device represented by handle 'h'. + * The 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'. + * --> When the call succeeds, the number of bytes returned can be anywhere in the range from 0 to 'maxlen' bytes. + * + * \return SANE_STATUS_GOOD (if everything is OK, otherwise, SANE_STATUS_NO_MEM/SANE_STATUS_INVAL) + */ +SANE_Status +sane_read(SANE_Handle h, SANE_Byte *buf, SANE_Int maxlen, SANE_Int *len) +{ + DBG (10, "escl sane_read\n"); + escl_sane_t *handler = h; + SANE_Status status = SANE_STATUS_GOOD; + long readbyte; + + if (!handler | !buf | !len) + return (SANE_STATUS_INVAL); + if (handler->cancel) + return (SANE_STATUS_CANCELLED); + if (!handler->write_scan_data) + handler->write_scan_data = SANE_TRUE; + if (!handler->decompress_scan_data) { + if (status != SANE_STATUS_GOOD) + return (status); + handler->decompress_scan_data = SANE_TRUE; + } + if (handler->scanner->img_data == NULL) + return (SANE_STATUS_INVAL); + if (!handler->end_read) { + readbyte = min((handler->scanner->img_size - handler->scanner->img_read), maxlen); + memcpy(buf, handler->scanner->img_data + handler->scanner->img_read, readbyte); + handler->scanner->img_read = handler->scanner->img_read + readbyte; + *len = readbyte; + if (handler->scanner->img_read == handler->scanner->img_size) + handler->end_read = SANE_TRUE; + else if (handler->scanner->img_read > handler->scanner->img_size) { + *len = 0; + handler->end_read = SANE_TRUE; + free(handler->scanner->img_data); + handler->scanner->img_data = NULL; + return (SANE_STATUS_INVAL); + } + } + else { + *len = 0; + free(handler->scanner->img_data); + handler->scanner->img_data = NULL; + return (SANE_STATUS_EOF); + } + return (SANE_STATUS_GOOD); +} + +SANE_Status +sane_get_select_fd(SANE_Handle __sane_unused__ h, SANE_Int __sane_unused__ *fd) +{ + return (SANE_STATUS_UNSUPPORTED); +} + +SANE_Status +sane_set_io_mode(SANE_Handle __sane_unused__ handle, SANE_Bool __sane_unused__ non_blocking) +{ + return (SANE_STATUS_UNSUPPORTED); +} |