/* Copyright (C) 2008, Panasonic Russia Ltd. Copyright (C) 2010-2011, m. allan noah */ /* sane - Scanner Access Now Easy. Panasonic KV-S1020C / KV-S1025C USB scanners. */ #define DEBUG_NOT_STATIC #include "../include/sane/config.h" #include #include #include #include #include #include #include #include #include #include #include "../include/sane/sane.h" #include "../include/sane/saneopts.h" #include "../include/sane/sanei.h" #include "../include/sane/sanei_usb.h" #include "../include/sane/sanei_backend.h" #include "../include/sane/sanei_config.h" #include "../include/lassert.h" #include "kvs1025.h" #include "kvs1025_low.h" #include "../include/sane/sanei_debug.h" /* SANE backend operations, see SANE Standard for details https://sane-project.gitlab.io/standard/ */ /* Init the KV-S1025 SANE backend. This function must be called before any other SANE function can be called. */ SANE_Status sane_init (SANE_Int * version_code, SANE_Auth_Callback __sane_unused__ authorize) { SANE_Status status; DBG_INIT (); DBG (DBG_sane_init, "sane_init\n"); DBG (DBG_error, "This is panasonic KV-S1020C / KV-S1025C version %d.%d build %d\n", V_MAJOR, V_MINOR, V_BUILD); if (version_code) { *version_code = SANE_VERSION_CODE (V_MAJOR, V_MINOR, V_BUILD); } /* Initialize USB */ sanei_usb_init (); status = kv_enum_devices (); if (status) return status; DBG (DBG_proc, "sane_init: leave\n"); return SANE_STATUS_GOOD; } /* Terminate the KV-S1025 SANE backend */ void sane_exit (void) { DBG (DBG_proc, "sane_exit: enter\n"); kv_exit (); DBG (DBG_proc, "sane_exit: exit\n"); } /* Get device list */ SANE_Status sane_get_devices (const SANE_Device *** device_list, SANE_Bool __sane_unused__ local_only) { DBG (DBG_proc, "sane_get_devices: enter\n"); kv_get_devices_list (device_list); DBG (DBG_proc, "sane_get_devices: leave\n"); return SANE_STATUS_GOOD; } /* Open device, return the device handle */ SANE_Status sane_open (SANE_String_Const devicename, SANE_Handle * handle) { return kv_open_by_name (devicename, handle); } /* Close device */ void sane_close (SANE_Handle handle) { DBG (DBG_proc, "sane_close: enter\n"); kv_close ((PKV_DEV) handle); DBG (DBG_proc, "sane_close: leave\n"); } /* Get option descriptor */ const SANE_Option_Descriptor * sane_get_option_descriptor (SANE_Handle handle, SANE_Int option) { return kv_get_option_descriptor ((PKV_DEV) handle, option); } /* Control option */ SANE_Status sane_control_option (SANE_Handle handle, SANE_Int option, SANE_Action action, void *val, SANE_Int * info) { return kv_control_option ((PKV_DEV) handle, option, action, val, info); } /* Get scan parameters */ SANE_Status sane_get_parameters (SANE_Handle handle, SANE_Parameters * params) { PKV_DEV dev = (PKV_DEV) handle; int side = dev->current_side == SIDE_FRONT ? 0 : 1; DBG (DBG_proc, "sane_get_parameters: enter\n"); if (!(dev->scanning)) { /* Setup the parameters for the scan. (guessed value) */ int resolution = dev->val[OPT_RESOLUTION].w; int width, length, depth = kv_get_depth (kv_get_mode (dev));; DBG (DBG_proc, "sane_get_parameters: initial settings\n"); kv_calc_paper_size (dev, &width, &length); DBG (DBG_error, "Resolution = %d\n", resolution); DBG (DBG_error, "Paper width = %d, height = %d\n", width, length); /* Prepare the parameters for the caller. */ dev->params[0].format = kv_get_mode (dev) == SM_COLOR ? SANE_FRAME_RGB : SANE_FRAME_GRAY; dev->params[0].last_frame = SANE_TRUE; dev->params[0].pixels_per_line = ((width * resolution) / 1200) & (~0xf); dev->params[0].depth = depth > 8 ? 8 : depth; dev->params[0].bytes_per_line = (dev->params[0].pixels_per_line / 8) * depth; dev->params[0].lines = (length * resolution) / 1200; memcpy (&dev->params[1], &dev->params[0], sizeof (SANE_Parameters)); } /* Return the current values. */ if (params) *params = (dev->params[side]); DBG (DBG_proc, "sane_get_parameters: exit\n"); return SANE_STATUS_GOOD; } /* Start scanning */ SANE_Status sane_start (SANE_Handle handle) { SANE_Status status; PKV_DEV dev = (PKV_DEV) handle; SANE_Bool dev_ready; KV_CMD_RESPONSE rs; DBG (DBG_proc, "sane_start: enter\n"); if (!dev->scanning) { /* open device */ if (!kv_already_open (dev)) { DBG (DBG_proc, "sane_start: need to open device\n"); status = kv_open (dev); if (status) { return status; } } /* Begin scan */ DBG (DBG_proc, "sane_start: begin scan\n"); /* Get necessary parameters */ sane_get_parameters (dev, NULL); dev->current_page = 0; dev->current_side = SIDE_FRONT; /* The scanner must be ready. */ status = CMD_test_unit_ready (dev, &dev_ready); if (status || !dev_ready) { return SANE_STATUS_DEVICE_BUSY; } if (!strcmp (dev->val[OPT_MANUALFEED].s, "off")) { status = CMD_get_document_existanse (dev); if (status) { DBG (DBG_proc, "sane_start: exit with no more docs\n"); return status; } } /* Set window */ status = CMD_reset_window (dev); if (status) { return status; } status = CMD_set_window (dev, SIDE_FRONT, &rs); if (status) { DBG (DBG_proc, "sane_start: error setting window\n"); return status; } if (rs.status) { DBG (DBG_proc, "sane_start: error setting window\n"); DBG (DBG_proc, "sane_start: sense_key=0x%x, ASC=0x%x, ASCQ=0x%x\n", get_RS_sense_key (rs.sense), get_RS_ASC (rs.sense), get_RS_ASCQ (rs.sense)); return SANE_STATUS_DEVICE_BUSY; } if (IS_DUPLEX (dev)) { status = CMD_set_window (dev, SIDE_BACK, &rs); if (status) { DBG (DBG_proc, "sane_start: error setting window\n"); return status; } if (rs.status) { DBG (DBG_proc, "sane_start: error setting window\n"); DBG (DBG_proc, "sane_start: sense_key=0x%x, " "ASC=0x%x, ASCQ=0x%x\n", get_RS_sense_key (rs.sense), get_RS_ASC (rs.sense), get_RS_ASCQ (rs.sense)); return SANE_STATUS_INVAL; } } /* Scan */ status = CMD_scan (dev); if (status) { return status; } status = AllocateImageBuffer (dev); if (status) { return status; } dev->scanning = 1; } else { /* renew page */ if (IS_DUPLEX (dev)) { if (dev->current_side == SIDE_FRONT) { /* back image data already read, so just return */ dev->current_side = SIDE_BACK; DBG (DBG_proc, "sane_start: duplex back\n"); status = SANE_STATUS_GOOD; goto cleanup; } else { dev->current_side = SIDE_FRONT; dev->current_page++; } } else { dev->current_page++; } } DBG (DBG_proc, "sane_start: NOW SCANNING page\n"); /* Read image data */ status = ReadImageData (dev, dev->current_page); if (status) { dev->scanning = 0; return status; } /* Get picture element size */ { int width, height; status = CMD_read_pic_elements (dev, dev->current_page, SIDE_FRONT, &width, &height); if (status) return status; } if (IS_DUPLEX (dev)) { int width, height; status = CMD_read_pic_elements (dev, dev->current_page, SIDE_BACK, &width, &height); if (status) return status; } /* software based enhancement functions from sanei_magic */ /* these will modify the image, and adjust the params */ /* at this point, we are only looking at the front image */ /* of simplex or duplex data, back side has already exited */ /* so, we do both sides now, if required */ if (dev->val[OPT_SWDESKEW].w){ buffer_deskew(dev,SIDE_FRONT); } if (dev->val[OPT_SWCROP].w){ buffer_crop(dev,SIDE_FRONT); } if (dev->val[OPT_SWDESPECK].w){ buffer_despeck(dev,SIDE_FRONT); } if (dev->val[OPT_SWDEROTATE].w || dev->val[OPT_ROTATE].w){ buffer_rotate(dev,SIDE_FRONT); } if (IS_DUPLEX (dev)){ if (dev->val[OPT_SWDESKEW].w){ buffer_deskew(dev,SIDE_BACK); } if (dev->val[OPT_SWCROP].w){ buffer_crop(dev,SIDE_BACK); } if (dev->val[OPT_SWDESPECK].w){ buffer_despeck(dev,SIDE_BACK); } if (dev->val[OPT_SWDEROTATE].w || dev->val[OPT_ROTATE].w){ buffer_rotate(dev,SIDE_BACK); } } cleanup: /* check if we need to skip this page */ if (dev->val[OPT_SWSKIP].w && buffer_isblank(dev,dev->current_side)){ DBG (DBG_proc, "sane_start: blank page, recurse\n"); return sane_start(handle); } DBG (DBG_proc, "sane_start: exit\n"); return status; } SANE_Status sane_read (SANE_Handle handle, SANE_Byte * buf, SANE_Int max_len, SANE_Int * len) { PKV_DEV dev = (PKV_DEV) handle; int side = dev->current_side == SIDE_FRONT ? 0 : 1; int size = max_len; if (!dev->scanning) return SANE_STATUS_EOF; if (size > dev->img_size[side]) size = dev->img_size[side]; if (size == 0) { *len = size; return SANE_STATUS_EOF; } if (dev->val[OPT_INVERSE].w && (kv_get_mode (dev) == SM_BINARY || kv_get_mode (dev) == SM_DITHER)) { int i; unsigned char *p = dev->img_pt[side]; for (i = 0; i < size; i++) { buf[i] = ~p[i]; } } else { memcpy (buf, dev->img_pt[side], size); } /*hexdump(DBG_error, "img data", buf, 128); */ dev->img_pt[side] += size; dev->img_size[side] -= size; DBG (DBG_proc, "sane_read: %d bytes to read, " "%d bytes read, EOF=%s %d\n", max_len, size, dev->img_size[side] == 0 ? "True" : "False", side); if (len) { *len = size; } if (dev->img_size[side] == 0) { if (!strcmp (dev->val[OPT_FEEDER_MODE].s, "single")) if ((IS_DUPLEX (dev) && side) || !IS_DUPLEX (dev)) dev->scanning = 0; } return SANE_STATUS_GOOD; } void sane_cancel (SANE_Handle handle) { PKV_DEV dev = (PKV_DEV) handle; DBG (DBG_proc, "sane_cancel: scan canceled.\n"); dev->scanning = 0; kv_close (dev); } SANE_Status sane_set_io_mode (SANE_Handle h, SANE_Bool m) { h=h; m=m; return SANE_STATUS_UNSUPPORTED; } SANE_Status sane_get_select_fd (SANE_Handle h, SANE_Int * fd) { h=h; fd=fd; return SANE_STATUS_UNSUPPORTED; }