/*******************************************************************************
 * SANE - Scanner Access Now Easy.

   avision.h

   This file is part of the SANE package.

   This program is free software; you can redistribute it and/or
   modify it under the terms of the GNU General Public License as
   published by the Free Software Foundation; either version 2 of the
   License, or (at your option) any later version.

   This program is distributed in the hope that it will be useful, but
   WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
   General Public License for more details.

   You should have received a copy of the GNU General Public License
   along with this program.  If not, see <https://www.gnu.org/licenses/>.

   As a special exception, the authors of SANE give permission for
   additional uses of the libraries contained in this release of SANE.

   The exception is that, if you link a SANE library with other files
   to produce an executable, this does not by itself cause the
   resulting executable to be covered by the GNU General Public
   License.  Your use of that executable is in no way restricted on
   account of linking the SANE library code into it.

   This exception does not, however, invalidate any other reasons why
   the executable file might be covered by the GNU General Public
   License.

   If you submit changes to SANE to the maintainers to be included in
   a subsequent release, you agree by submitting the changes that
   those changes may be distributed with this exception intact.

   *****************************************************************************

   This backend is based upon the Tamarack backend and adapted to the Avision
   scanners by René Rebe and Meino Cramer.

   Check the avision.c file for detailed copyright and change-log
   information.

********************************************************************************/

#ifndef avision_h
#define avision_h

#ifdef HAVE_STDINT_H
# include <stdint.h>            /* available in ISO C99 */
#else
# include <sys/types.h>
typedef uint8_t uint8_t;
typedef uint16_t uint16_t;
typedef uint32_t uint32_t;
#endif /* HAVE_STDINT_H */

#ifndef PATH_MAX
# define PATH_MAX 1024
#endif

typedef enum Avision_ConnectionType {
  AV_SCSI,
  AV_USB
} Avision_ConnectionType;

/*
 * Translatable custom options text.
 *
 */
#define SANE_TITLE_MISC_GROUP             SANE_I18N("Miscellaneous")
#define SANE_TITLE_INSTALLED_OPTS_GROUP   SANE_I18N("Installed options")

#define SANE_TITLE_OVERSCAN_TOP           SANE_I18N("Overscan top")
#define SANE_TITLE_OVERSCAN_BOTTOM        SANE_I18N("Overscan bottom")
#define SANE_TITLE_BACKGROUND_LINES       SANE_I18N("Background raster lines")
#define SANE_TITLE_QUALITY_SCAN           SANE_I18N("Quality scan")
#define SANE_TITLE_MANUAL_EXPOSURE        SANE_I18N("Exposure")
#define SANE_TITLE_MULTI_SAMPLE           SANE_I18N("Multi-sample")
#define SANE_TITLE_POWER_SAVE_TIME        SANE_I18N("Power save timer control")
#define SANE_TITLE_OPTIONS_MSG            SANE_I18N("Message text from the scanner")
#define SANE_TITLE_NVRAM                  SANE_I18N("Obtain NVRAM values")
#define SANE_TITLE_PAPER_LENGTH           SANE_I18N("Use paper length")
#define SANE_TITLE_FLIP_PAGE              SANE_I18N("Flip document after duplex scanning")
#define SANE_TITLE_ADF_INSTALLED          SANE_I18N("ADF installed")
#define SANE_TITLE_LIGHTBOX_INSTALLED     SANE_I18N("Lightbox installed")

#define SANE_DESC_OVERSCAN_TOP            \
SANE_I18N("The top overscan controls the additional area to scan before the "\
          "paper is detected.")
#define SANE_DESC_OVERSCAN_BOTTOM         \
SANE_I18N("The bottom overscan controls the additional area to scan after "\
          "the paper end is detected.")
#define SANE_DESC_BACKGROUND_LINES        \
SANE_I18N("The background raster controls the additional background lines to "\
          "scan before the paper is feed through the scanner.")
#define SANE_DESC_QUALITY_SCAN            \
SANE_I18N("Turn on quality scanning (slower but better).")
#define SANE_DESC_MANUAL_EXPOSURE         \
SANE_I18N("Manual exposure adjustment.")
#define SANE_DESC_MULTI_SAMPLE            \
SANE_I18N("Enable multi-sample scan mode.")
#define SANE_DESC_POWER_SAVE_TIME         \
SANE_I18N("Allows control of the scanner's power save timer, dimming or "\
          "turning off the light.")
#define SANE_DESC_OPTIONS_MSG             \
SANE_I18N("This text contains device specific options controlled by the "\
          "user on the scanner hardware.")
#define SANE_DESC_NVRAM                   \
SANE_I18N("Allows access obtaining the scanner's NVRAM values as pretty "\
          "printed text.")
#define SANE_DESC_PAPER_LENGTH            \
SANE_I18N("Newer scanners can utilize this paper length to detect double feeds. "\
          "However some others (DM152) can get confused during media flush if it is set.")
#define SANE_DESC_FLIP_PAGE               \
SANE_I18N("Tells page-flipping document scanners to flip the paper back to its "\
          "original orientation before dropping it in the output tray.  "\
          "Turning this off might make scanning a little faster if you don't "\
          "care about manually flipping the pages afterwards.")
#define SANE_DESC_ADF_INSTALLED           \
SANE_I18N("ADF option is detected as installed.")
#define SANE_DESC_LIGHTBOX_INSTALLED      \
SANE_I18N("Lightbox option is detected as installed.")


/* information needed for device access */
typedef struct Avision_Connection {
  Avision_ConnectionType connection_type;
  int scsi_fd;			/* SCSI filedescriptor */
  SANE_Int usb_dn;		/* USB (libusb or scanner.c) device number */
  enum {
    AVISION_USB_UNTESTED_STATUS, /* status type untested */
    AVISION_USB_INT_STATUS,      /* interrupt endp. (USB 1.x device) status */
    AVISION_USB_BULK_STATUS      /* bulk endp. (USB 2.0 device) status */
  } usb_status;

} Avision_Connection;

/* structure for ADF offsets in mm */
typedef struct mm_offset {
  double top;
  double bottom;
} mm_offset;

typedef struct Avision_HWEntry {
  const char* scsi_mfg;
  const char* scsi_model;

  int   usb_vendor;
  int   usb_product;

  const char* real_mfg;
  const char* real_model;

  /* feature overwrites - as embedded CPUs have 16bit enums - this
     would need a change ... */
    /* force no calibration */
  #define AV_NO_CALIB ((uint64_t)1<<0)

    /* force all in one command calibration */
  #define AV_ONE_CALIB_CMD ((uint64_t)1<<1)

    /* no gamma table */
  #define AV_NO_GAMMA ((uint64_t)1<<2)

    /* light check is bogus */
  #define AV_LIGHT_CHECK_BOGUS ((uint64_t)1<<3)

    /* no button though the device advertise it */
  #define AV_NO_BUTTON ((uint64_t)1<<4)

    /* if the scan area needs to be forced to A3 */
  #define AV_FORCE_A3 ((uint64_t)1<<5)

    /* if the scan area and resolution needs to be forced for films */
  #define AV_FORCE_FILM ((uint64_t)1<<6)

    /* does not support, or very broken background (added for AV610C2) */
  #define AV_NO_BACKGROUND ((uint64_t)1<<7)

    /* is film scanner - no detection yet */
  #define AV_FILMSCANNER ((uint64_t)1<<8)

    /* fujitsu adaption */
  #define AV_FUJITSU ((uint64_t)1<<9)

    /* gray calibration data has to be uploaded on the blue channel ... ? */
  #define AV_GRAY_CALIB_BLUE ((uint64_t)1<<10)

    /* Interrupt endpoint button readout (so far AV220) */
  #define AV_INT_BUTTON ((uint64_t)1<<11)

    /* send acceleration table ... */
  #define AV_ACCEL_TABLE ((uint64_t)1<<12)

    /* non-interlaced scanns up to 300 dpi (AV32xx / AV83xx) */
  #define AV_NON_INTERLACED_DUPLEX_300 ((uint64_t)1<<13)

    /* do not read multiples of 64 bytes - stalls the USB chip */
  #define AV_NO_64BYTE_ALIGN ((uint64_t)1<<14)

    /* force channel-by-channel calibration */
  #define AV_MULTI_CALIB_CMD ((uint64_t)1<<15)

    /* non color scans are faster with a filter applied (AV32xx) */
  #define AV_FASTER_WITH_FILTER ((uint64_t)1<<16)

    /* interlaced data with 1 line distance */
  #define AV_2ND_LINE_INTERLACED ((uint64_t)1<<17)

    /* does not keep the window though it advertices so */
  #define AV_DOES_NOT_KEEP_WINDOW ((uint64_t)1<<18)

    /* does not keep the gamma though it advertices so */
  #define AV_DOES_NOT_KEEP_GAMMA ((uint64_t)1<<19)

    /* advertises ADF is BGR order, but isn't (or vice versa) */
  #define AV_ADF_BGR_ORDER_INVERT ((uint64_t)1<<20)

    /* allows 12bit mode, though not flagged */
  #define AV_12_BIT_MODE ((uint64_t)1<<21)

    /* very broken background raster */
  #define AV_BACKGROUND_QUIRK ((uint64_t)1<<22)

    /* though marked as GRAY only the scanner can do GRAY modes */
  #define AV_GRAY_MODES ((uint64_t)1<<23)

    /* no separate, single REAR scan (AV122, DM152, ...) */
  #define AV_NO_REAR ((uint64_t)1<<24)

    /* only scan with some known good hardware resolutions, as the
       scanner fails to properly interpoloate in between (e.g.  AV121,
       DM152 on duplex scans - but also the AV600), software scale and
       interpolate to all the others */
  #define AV_SOFT_SCALE ((uint64_t)1<<25)

    /* does keep window though it does not advertise it - the AV122/DM152
       mess up image data if window is resend between ADF pages */
  #define AV_DOES_KEEP_WINDOW ((uint64_t)1<<26)

    /* does keep gamma though it does not advertise it */
  #define AV_DOES_KEEP_GAMMA ((uint64_t)1<<27)

    /* does the scanner contain a Cancel button? */
  #define AV_CANCEL_BUTTON ((uint64_t)1<<28)

    /* some devices do not need a START_SCAN, even hang with it */
  #define AV_NO_START_SCAN ((uint64_t)1<<30)

  #define AV_INT_STATUS ((uint64_t)1<<31)

    /* force no calibration */
  #define AV_NO_TUNE_SCAN_LENGTH ((uint64_t)1<<32)

    /* for gray scans, set grey filter */
  #define AV_USE_GRAY_FILTER ((uint64_t)1<<33)

    /* For (HP) scanners with flipping duplexers */
  #define AV_ADF_FLIPPING_DUPLEX ((uint64_t)1<<34)

    /* For scanners which need to have their firmware read to properly function. */
  #define AV_FIRMWARE ((uint64_t)1<<35)

  /* at least Kodak i1120 claims no calibration needed but windows driver does it anyways */
  #define AV_FORCE_CALIB ((uint64_t)1<<36)

  /* at least Kodak i1120 does not have an explicit "quality-scan" mode */
  #define AV_NO_QSCAN_MODE ((uint64_t)1<<37)

  /* at least Kodak i1120 optical DPI is used for overscan calculation */
  #define AV_OVERSCAN_OPTDPI ((uint64_t)1<<38)

  /* some scanners support fast feed-out of the sheet when cancelling a running scan */
  #define AV_FASTFEED_ON_CANCEL ((uint64_t)1<<39)

  /* at least Kodak i1120 does not have an explicit "quality-calibration" mode */
  #define AV_NO_QCALIB_MODE ((uint64_t)1<<40)

  /* Kodak i1120 needs gamma = 1.0 to give decent results */
  #define AV_GAMMA_10 ((uint64_t)1<<41)

  /* Kodak i1120 has a different gamma table format (like a uint16/double array) */
  #define AV_GAMMA_UINT16 ((uint64_t)1<<42)

  /* Kodak i1120 has single-sheet and multi-sheet scan modes. This option sets
     bitset3[7] which enables multi-sheet scan by default so there is no pause
     of 1s between two sheets in ADF scan mode. This also fixes some offsets
     when scanning multiple sheets. */
  #define AV_MULTI_SHEET_SCAN ((uint64_t)1<<43)

    /* maybe more ...*/
  uint64_t feature_type;

  /* ADF offsets in mm */
  struct {
    float first; /* offset difference first sheet */
    mm_offset front; /* front-only */
    struct {
      mm_offset front;
      mm_offset rear;
    } duplex;
  } offset;

} Avision_HWEntry;

typedef enum {
  AV_ASIC_Cx = 0,
  AV_ASIC_C1 = 1,
  AV_ASIC_W1 = 2,
  AV_ASIC_C2 = 3,
  AV_ASIC_C5 = 5,
  AV_ASIC_C6 = 6,
  AV_ASIC_C7 = 7,
  AV_ASIC_OA980 = 128,
  AV_ASIC_OA982 = 129
} asic_type;

typedef enum {
  AV_THRESHOLDED,
  AV_DITHERED,
  AV_GRAYSCALE,       /* all gray needs to be before color for is_color() */
  AV_GRAYSCALE12,
  AV_GRAYSCALE16,
  AV_TRUECOLOR,
  AV_TRUECOLOR12,
  AV_TRUECOLOR16,
  AV_COLOR_MODE_LAST
} color_mode;

typedef enum {
  AV_NORMAL,
  AV_TRANSPARENT,
  AV_ADF,
  AV_ADF_REAR,
  AV_ADF_DUPLEX,
  AV_SOURCE_MODE_LAST
} source_mode;

typedef enum {
  AV_NORMAL_DIM,
  AV_TRANSPARENT_DIM,
  AV_ADF_DIM,
  AV_SOURCE_MODE_DIM_LAST
} source_mode_dim;

enum Avision_Option
{
  OPT_NUM_OPTS = 0,      /* must come first */

  OPT_MODE_GROUP,
  OPT_MODE,
  OPT_RESOLUTION,
#define OPT_RESOLUTION_DEFAULT 150
  OPT_SPEED,
  OPT_PREVIEW,

  OPT_SOURCE,            /* scan source normal, transparency, ADF */

  OPT_GEOMETRY_GROUP,
  OPT_TL_X,	         /* top-left x */
  OPT_TL_Y,	         /* top-left y */
  OPT_BR_X,	         /* bottom-right x */
  OPT_BR_Y,		 /* bottom-right y */

  OPT_OVERSCAN_TOP,      /* overscan for auto-crop/deskew, if supported */
  OPT_OVERSCAN_BOTTOM,
  OPT_BACKGROUND,        /* background raster lines to read out */

  OPT_ENHANCEMENT_GROUP,
  OPT_BRIGHTNESS,
  OPT_CONTRAST,
  OPT_QSCAN,
  OPT_QCALIB,

  OPT_GAMMA_VECTOR,      /* first must be gray */
  OPT_GAMMA_VECTOR_R,    /* then r g b vector */
  OPT_GAMMA_VECTOR_G,
  OPT_GAMMA_VECTOR_B,

  OPT_EXPOSURE,          /* film exposure adjustment */
  OPT_IR,                /* infra-red */
  OPT_MULTISAMPLE,       /* multi-sample */

  OPT_MISC_GROUP,
  OPT_FRAME,             /* Film holder control */

  OPT_POWER_SAVE_TIME,   /* set power save time to the scanner */

  OPT_MESSAGE,           /* optional message from the scanner display */
  OPT_NVRAM,             /* retrieve NVRAM values as pretty printed text */

  OPT_PAPERLEN,          /* Use paper_length field to detect double feeds */
  OPT_ADF_FLIP,          /* For flipping duplex, reflip the document */

  OPT_OPTIONS_GROUP,
  OPT_OPTION_ADF,       // ADF installed/detected?
  OPT_OPTION_LIGHTBOX,   // LightBox installed/detected?

  NUM_OPTIONS            /* must come last */
};

/* structure for ADF offsets in pixels of HW res */
typedef struct hwpx_offset {
  int top;
  int bottom;
} hwpx_offset;


typedef struct Avision_Dimensions
{
  /* in dpi */
  int xres;
  int yres;

  /* in pixels */
  long tlx;
  long tly;
  long brx;
  long bry;

  /* in pixels */
  int line_difference;

  struct {
    hwpx_offset front;
    hwpx_offset rear;
  } offset;

  /* interlaced duplex scan */
  SANE_Bool interlaced_duplex;

  /* in dpi, likewise - different if software scaling required */
  int hw_xres;
  int hw_yres;

  int hw_pixels_per_line;
  int hw_bytes_per_line;
  int hw_lines;

} Avision_Dimensions;

/* this contains our low-level info - not relevant for the SANE interface  */
typedef struct Avision_Device
{
  struct Avision_Device* next;
  SANE_Device sane;
  Avision_Connection connection;

  /* structs used to store config options */
  SANE_Range dpi_range;
  SANE_Range x_range;
  SANE_Range y_range;
  SANE_Range speed_range;

  asic_type inquiry_asic_type;
  SANE_Bool inquiry_new_protocol;

  SANE_Bool inquiry_nvram_read;
  SANE_Bool inquiry_power_save_time;

  SANE_Bool inquiry_adf_capability;
  SANE_Bool inquiry_duplex;
  SANE_Bool inquiry_duplex_interlaced;
  SANE_Bool inquiry_paper_length;
  SANE_Bool inquiry_batch_scan;
  SANE_Bool inquiry_detect_accessories;
  SANE_Bool inquiry_needs_calibration;
  SANE_Bool inquiry_needs_gamma;
  SANE_Bool inquiry_keeps_gamma;
  SANE_Bool inquiry_keeps_window;
  SANE_Bool inquiry_calibration;
  SANE_Bool inquiry_3x3_matrix;
  SANE_Bool inquiry_needs_software_colorpack;
  SANE_Bool inquiry_needs_line_pack;
  SANE_Bool inquiry_adf_need_mirror;
  SANE_Bool inquiry_adf_bgr_order;
  SANE_Bool inquiry_light_detect;
  SANE_Bool inquiry_light_control;
  SANE_Bool inquiry_exposure_control;

  // Determines from accessories query.
  SANE_Bool inquiry_light_box_present;
  SANE_Bool inquiry_adf_present;

  int       inquiry_max_shading_target;
  SANE_Bool inquiry_button_control;
  unsigned int inquiry_buttons;
  SANE_Bool inquiry_tune_scan_length;
  SANE_Bool inquiry_background_raster;
  int       inquiry_background_raster_pixel;

  enum {AV_FLATBED,
	AV_FILM,
	AV_SHEETFEED
  } scanner_type;

  /* the list of available color modes */
  SANE_String_Const color_list[AV_COLOR_MODE_LAST + 1];
  color_mode color_list_num[AV_COLOR_MODE_LAST];
  color_mode color_list_default;

  /* the list of available source modes */
  SANE_String_Const source_list[AV_SOURCE_MODE_LAST + 1];
  source_mode source_list_num[AV_SOURCE_MODE_LAST];

  int inquiry_optical_res;        /* in dpi */
  int inquiry_max_res;            /* in dpi */

  double inquiry_x_ranges  [AV_SOURCE_MODE_DIM_LAST]; /* in mm */
  double inquiry_y_ranges  [AV_SOURCE_MODE_DIM_LAST]; /* in mm */

  int inquiry_color_boundary;
  int inquiry_gray_boundary;
  int inquiry_dithered_boundary;
  int inquiry_thresholded_boundary;
  int inquiry_line_difference; /* software color pack */

  int inquiry_channels_per_pixel;
  int inquiry_bits_per_channel;
  int inquiry_no_gray_modes;

  SANE_Bool adf_offset_compensation;

  int scsi_buffer_size; /* nice to have SCSI buffer size */
  int read_stripe_size; /* stripes to be read at-a-time */

  /* film scanner attributes - maybe these should be in the scanner struct? */
  SANE_Range frame_range;
  SANE_Word current_frame;
  SANE_Word holder_type;

  /* some version corrections */
  uint16_t data_dq; /* was ox0A0D - but hangs some new scanners */

  Avision_HWEntry* hw;
} Avision_Device;

/* all the state relevant for the SANE interface */
typedef struct Avision_Scanner
{
  struct Avision_Scanner* next;
  Avision_Device* hw;

  SANE_Option_Descriptor opt [NUM_OPTIONS];
  Option_Value val [NUM_OPTIONS];
  SANE_Int gamma_table [4][256];

  /* we now save the calib data because we might need it for 16bit software
     calibration :-( */
  uint8_t* dark_avg_data;
  uint8_t* white_avg_data;

  /* background raster data, if duplex first front, then rear */
  uint8_t* background_raster;

  /* Parsed option values and variables that are valid only during
     the actual scan: */
  SANE_Bool prepared;		/* first page marker */
  SANE_Bool scanning;           /* scan in progress */
  unsigned int page;            /* page counter, 0: uninitialized, 1: scanning 1st page, ... */
  int cancelled;

  SANE_Parameters params;       /* scan window */
  Avision_Dimensions avdimen;   /* scan window - detailed internals */

  /* Internal data for duplex scans */
  char duplex_rear_fname [PATH_MAX];
  SANE_Bool duplex_rear_valid;

  color_mode c_mode;
  source_mode source_mode;
  source_mode_dim source_mode_dim;

  /* Avision HW Access Connection (SCSI/USB abstraction) */
  Avision_Connection av_con;

  SANE_Pid reader_pid;	/* process id of reader */
  int read_fds;		/* pipe reading end */
  int write_fds;	/* pipe writing end */

} Avision_Scanner;

/* Some Avision driver internal defines */
#define AV_WINID 0

/* Avision SCSI over USB error codes */
#define AVISION_USB_GOOD                    0x00
#define AVISION_USB_REQUEST_SENSE           0x02
#define AVISION_USB_BUSY                    0x08

/* SCSI commands that the Avision scanners understand: */

#define AVISION_SCSI_TEST_UNIT_READY        0x00
#define AVISION_SCSI_REQUEST_SENSE          0x03
#define AVISION_SCSI_MEDIA_CHECK            0x08
#define AVISION_SCSI_INQUIRY                0x12
#define AVISION_SCSI_MODE_SELECT            0x15
#define AVISION_SCSI_RESERVE_UNIT           0x16
#define AVISION_SCSI_RELEASE_UNIT           0x17
#define AVISION_SCSI_SCAN                   0x1b
#define AVISION_SCSI_SET_WINDOW             0x24
#define AVISION_SCSI_READ                   0x28
#define AVISION_SCSI_SEND                   0x2a
#define AVISION_SCSI_OBJECT_POSITION        0x31
#define AVISION_SCSI_GET_DATA_STATUS        0x34

#define AVISION_SCSI_OP_REJECT_PAPER        0x00
#define AVISION_SCSI_OP_LOAD_PAPER          0x01
#define AVISION_SCSI_OP_GO_HOME             0x02
#define AVISION_SCSI_OP_TRANS_CALIB_GRAY    0x04
#define AVISION_SCSI_OP_TRANS_CALIB_COLOR   0x05

/* These apply to bitset1.  The values are 0 to 6, shifted 3 bits to the left */
#define AVISION_FILTER_NONE	0x00
#define AVISION_FILTER_RED	0x08
#define AVISION_FILTER_GREEN	0x10
#define AVISION_FILTER_BLUE	0x18
#define AVISION_FILTER_RGB	0x20
#define AVISION_FILTER_CMYK	0x28
#define AVISION_FILTER_GRAY	0x30

/* The SCSI structures that we have to send to an avision to get it to
   do various stuff... */

typedef struct command_header
{
  uint8_t opc;
  uint8_t pad0 [3];
  uint8_t len;
  uint8_t pad1;
} command_header;

typedef struct command_set_window
{
  uint8_t opc;
  uint8_t reserved0 [5];
  uint8_t transferlen [3];
  uint8_t control;
} command_set_window;

typedef struct command_read
{
  uint8_t opc;
  uint8_t bitset1;
  uint8_t datatypecode;
  uint8_t readtype;
  uint8_t datatypequal [2];
  uint8_t transferlen [3];
  uint8_t control;
} command_read;

typedef struct command_scan
{
  uint8_t opc;
  uint8_t bitset0;
  uint8_t reserved0 [2];
  uint8_t transferlen;
  uint8_t bitset1;
} command_scan;

typedef struct command_send
{
  uint8_t opc;
  uint8_t bitset1;
  uint8_t datatypecode;
  uint8_t reserved0;
  uint8_t datatypequal [2];
  uint8_t transferlen [3];
  uint8_t reserved1;
} command_send;

typedef struct firmware_status
{
  uint8_t download_firmware;
  uint8_t first_effective_pixel_flatbed [2];
  uint8_t first_effective_pixel_adf_front [2];
  uint8_t first_effective_pixel_adf_rear [2];
  uint8_t reserved;
} firmware_status;

typedef struct nvram_data
{
  uint8_t pad_scans [4];
  uint8_t adf_simplex_scans [4];
  uint8_t adf_duplex_scans [4];
  uint8_t flatbed_scans [4];

  uint8_t flatbed_leading_edge [2];
  uint8_t flatbed_side_edge [2];
  uint8_t adf_leading_edge [2];
  uint8_t adf_side_edge [2];
  uint8_t adf_rear_leading_edge [2];
  uint8_t adf_rear_side_edge [2];

  uint8_t born_month [2];
  uint8_t born_day [2];
  uint8_t born_year [2];

  uint8_t first_scan_month [2];
  uint8_t first_scan_day [2];
  uint8_t first_scan_year [2];

  uint8_t vertical_magnification [2];
  uint8_t horizontal_magnification [2];

  uint8_t ccd_type;
  uint8_t scan_speed;

  char     serial [24];

  uint8_t power_saving_time [2];

  uint8_t auto_feed;
  uint8_t roller_count [4];
  uint8_t multifeed_count [4];
  uint8_t jam_count [4];

  uint8_t reserved;
  char     identify_info[16];
  char     formal_name[16];

  uint8_t reserved2 [10];
} nvram_data;

typedef struct command_set_window_window
{
  struct {
    uint8_t reserved0 [6];
    uint8_t desclen [2];
  } header;

  struct {
    uint8_t winid;
    uint8_t reserved0;
    uint8_t xres [2];
    uint8_t yres [2];
    uint8_t ulx [4];
    uint8_t uly [4];
    uint8_t width [4];
    uint8_t length [4];
    uint8_t brightness;
    uint8_t threshold;
    uint8_t contrast;
    uint8_t image_comp;
    uint8_t bpc;
    uint8_t halftone [2];
    uint8_t padding_and_bitset;
    uint8_t bitordering [2];
    uint8_t compr_type;
    uint8_t compr_arg;
    uint8_t paper_length[2];
    uint8_t reserved1 [4];

    /* Avision specific parameters */
    uint8_t vendor_specific;
    uint8_t paralen; /* bytes following after this byte */
  } descriptor;

  struct {
    uint8_t bitset1;
    uint8_t highlight;
    uint8_t shadow;
    uint8_t line_width [2];
    uint8_t line_count [2];

    /* the tail is quite version and model specific */
    union {
      struct {
	uint8_t bitset2;
	uint8_t reserved;
      } old;

      struct {
	uint8_t bitset2;
	uint8_t ir_exposure_time;

	/* optional */
	uint8_t r_exposure_time [2];
	uint8_t g_exposure_time [2];
	uint8_t b_exposure_time [2];

	uint8_t bitset3; /* reserved in the v2 */
	uint8_t auto_focus;
	uint8_t line_width_msb;
	uint8_t line_count_msb;
	uint8_t background_lines;

	uint8_t single_sheet_scan; /* from Kodak SVT tool */
      } normal;

      struct {
	uint8_t reserved0 [4];
	uint8_t paper_size;
	uint8_t paperx [4];
	uint8_t papery [4];
	uint8_t reserved1 [2];
      } fujitsu;
    } type;
  } avision;
} command_set_window_window;

typedef struct page_header
{
  uint8_t pad0 [4];
  uint8_t code;
  uint8_t length;
} page_header;

typedef struct avision_page
{
  uint8_t gamma;
  uint8_t thresh;
  uint8_t masks;
  uint8_t delay;
  uint8_t features;
  uint8_t pad0;
} avision_page;

typedef struct calibration_format
{
  uint16_t pixel_per_line;
  uint8_t bytes_per_channel;
  uint8_t lines;
  uint8_t flags;
  uint8_t ability1;
  uint8_t r_gain;
  uint8_t g_gain;
  uint8_t b_gain;
  uint16_t r_shading_target;
  uint16_t g_shading_target;
  uint16_t b_shading_target;
  uint16_t r_dark_shading_target;
  uint16_t g_dark_shading_target;
  uint16_t b_dark_shading_target;

  /* not returned but useful in some places */
  uint8_t channels;
} calibration_format;

typedef struct matrix_3x3
{
  uint16_t v[9];
} matrix_3x3;

typedef struct acceleration_info
{
  uint16_t total_steps;
  uint16_t stable_steps;
  uint32_t table_units;
  uint32_t base_units;
  uint16_t start_speed;
  uint16_t target_speed;
  uint8_t ability;
  uint8_t table_count;
  uint8_t reserved[6];
} acceleration_info;

/* set/get SCSI highended (big-endian) variables. Declare them as an array
 * of chars endianness-safe, int-size safe ... */
#define set_double(var,val) var[0] = (uint8_t) (((val) >> 8) & 0xff);  \
                            var[1] = (uint8_t) (((val)     ) & 0xff)

#define set_triple(var,val) var[0] = (uint8_t) (((val) >> 16) & 0xff); \
                            var[1] = (uint8_t) (((val) >> 8 ) & 0xff); \
                            var[2] = (uint8_t) (((val)      ) & 0xff)

#define set_quad(var,val)   var[0] = (uint8_t) (((val) >> 24) & 0xff); \
                            var[1] = (uint8_t) (((val) >> 16) & 0xff); \
                            var[2] = (uint8_t) (((val) >> 8 ) & 0xff); \
                            var[3] = (uint8_t) (((val)      ) & 0xff)

#define get_double(var) ((*var << 8) + *(var + 1))

#define get_triple(var) ((*var << 16) + \
                         (*(var + 1) << 8) + *(var + 2))

#define get_quad(var)   ((*var << 24) + \
                         (*(var + 1) << 16) + \
                         (*(var + 2) << 8) + *(var + 3))

/* set/get Avision lowended (little-endian) shading data */
#define set_double_le(var,val) var[0] = (uint8_t) (((val)     ) & 0xff);  \
                               var[1] = (uint8_t) (((val) >> 8) & 0xff)

#define get_double_le(var) ((uint16_t) ((*(var + 1) << 8) + *(var)))

#define BIT(n, p) ((n & (1 << p)) ? 1 : 0)

#define SET_BIT(n, p) (n |= (1 << p))
#define CLEAR_BIT(n, p) (n &= ~(1 << p))

/* These should be in saneopts.h */
#define SANE_NAME_FRAME "frame"
#define SANE_TITLE_FRAME SANE_I18N("Number of the frame to scan")
#define SANE_DESC_FRAME  SANE_I18N("Selects the number of the frame to scan")

#define SANE_NAME_DUPLEX "duplex"
#define SANE_TITLE_DUPLEX SANE_I18N("Duplex scan")
#define SANE_DESC_DUPLEX SANE_I18N("Duplex scan provides a scan of the front and back side of the document")

#ifdef AVISION_ENHANCED_SANE
#warning "Compiled Avision backend will violate the SANE standard"
/* Some Avision SANE extensions */
typedef enum
{
  SANE_STATUS_LAMP_WARMING = SANE_STATUS_ACCESS_DENIED + 1	/* lamp is warming up */
}
SANE_Avision_Status;

/* public API extension */

extern SANE_Status ENTRY(media_check) (SANE_Handle handle);

#endif

typedef enum
{
  AVISION_DATATYPECODE_READ_IMAGE_DATA = 0x00,
  AVISION_DATATYPECODE_GET_CALIBRATION_FORMAT = 0x60,
  AVISION_DATATYPECODE_DETECT_ACCESSORIES = 0x64,
  AVISION_DATATYPECODE_READ_NVRAM_DATA = 0x69,
  AVISION_DATATYPECODE_FLASH_RAM_INFO = 0x6a,
  AVISION_DATATYPECODE_ACCELERATION_TABLE = 0x6c,
  AVISION_DATATYPECODE_DOWNLOAD_GAMMA_TABLE = 0x81,
  AVISION_DATATYPECODE_3X3_COLOR_MATRIX = 0x83,
  AVISION_DATATYPECODE_SEND_NVRAM_DATA = 0x85,
  AVISION_DATATYPECODE_FLASH_DATA = 0x86,
  AVISION_DATATYPECODE_FILM_HOLDER_SENSE = 0x87,
  AVISION_DATATYPECODE_FIRMWARE_STATUS = 0x90,
  AVISION_DATATYPECODE_ATTACH_TRUNCATE_TAIL = 0x95,
  AVISION_DATATYPECODE_ATTACH_TRUNCATE_HEAD = 0x96,
  AVISION_DATATYPECODE_GET_BACKGROUND_RASTER = 0x9b,
  AVISION_DATATYPECODE_LIGHT_STATUS = 0xa0,
  AVISION_DATATYPECODE_BUTTON_STATUS = 0xa1,
  AVISION_DATATYPECODE_POWER_SAVING_TIMER = 0xa2,
  AVISION_DATATYPECODE_READ_DUPLEX_INFO = 0xb1,
  AVISION_DATATYPECODE_UNKNOWN = 0xd0,
  AVISION_DATATYPECODE_READ_GENERAL_ABILITY_PARAM = 0xd2,
} Avision_Datatypecode;

#endif /* avision_h */
