kopia lustrzana https://gitlab.com/sane-project/backends
1405 wiersze
41 KiB
C
1405 wiersze
41 KiB
C
/* sane - Scanner Access Now Easy.
|
|
Copyright (C) 1998 David F. Skoll
|
|
Heavily based on "hp.c" driver for HP Scanners, by
|
|
David Mosberger-Tang.
|
|
|
|
This file is part of the SANE package.
|
|
|
|
This program is free software; you can redistribute it and/or
|
|
modify it under the terms of the GNU General Public License as
|
|
published by the Free Software Foundation; either version 2 of the
|
|
License, or (at your option) any later version.
|
|
|
|
This program is distributed in the hope that it will be useful, but
|
|
WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
General Public License for more details.
|
|
|
|
You should have received a copy of the GNU General Public License
|
|
along with this program; if not, write to the Free Software
|
|
Foundation, Inc., 59 Temple Place - Suite 330, Boston,
|
|
MA 02111-1307, USA.
|
|
|
|
As a special exception, the authors of SANE give permission for
|
|
additional uses of the libraries contained in this release of SANE.
|
|
|
|
The exception is that, if you link a SANE library with other files
|
|
to produce an executable, this does not by itself cause the
|
|
resulting executable to be covered by the GNU General Public
|
|
License. Your use of that executable is in no way restricted on
|
|
account of linking the SANE library code into it.
|
|
|
|
This exception does not, however, invalidate any other reasons why
|
|
the executable file might be covered by the GNU General Public
|
|
License.
|
|
|
|
If you submit changes to SANE to the maintainers to be included in
|
|
a subsequent release, you agree by submitting the changes that
|
|
those changes may be distributed with this exception intact.
|
|
|
|
If you write modifications of your own for SANE, it is your choice
|
|
whether to permit this exception to apply to your modifications.
|
|
If you do not wish that, delete this exception notice.
|
|
|
|
This file implements a SANE backend for the Polaroid Digital
|
|
Microscope Camera. */
|
|
|
|
/* $Id$ */
|
|
|
|
#include "sane/config.h"
|
|
|
|
#include <limits.h>
|
|
#include <stdlib.h>
|
|
#include <stdarg.h>
|
|
#include <string.h>
|
|
|
|
#include "_stdint.h"
|
|
|
|
#include "sane/sane.h"
|
|
#include "sane/saneopts.h"
|
|
#include "sane/sanei_scsi.h"
|
|
|
|
#define BACKEND_NAME dmc
|
|
#include "sane/sanei_backend.h"
|
|
|
|
#ifndef PATH_MAX
|
|
# define PATH_MAX 1024
|
|
#endif
|
|
|
|
#include "sane/sanei_config.h"
|
|
#define DMC_CONFIG_FILE "dmc.conf"
|
|
|
|
#include "dmc.h"
|
|
|
|
/* A linked-list of attached devices and handles */
|
|
static DMC_Device *FirstDevice = NULL;
|
|
static DMC_Camera *FirstHandle = NULL;
|
|
static int NumDevices = 0;
|
|
|
|
static SANE_String_Const ValidModes[] = { "Full frame", "Viewfinder",
|
|
"Raw", "Thumbnail",
|
|
"Super-Resolution",
|
|
NULL };
|
|
|
|
static SANE_String_Const ValidBalances[] = { "Daylight", "Incandescent",
|
|
"Fluorescent", NULL };
|
|
|
|
static SANE_Word ValidASAs[] = { 3, 25, 50, 100 };
|
|
|
|
/* Convert between 32-us ticks and milliseconds */
|
|
#define MS_TO_TICKS(x) (((x) * 1000 + 16) / 32)
|
|
#define TICKS_TO_MS(x) (((x) * 32) / 1000)
|
|
|
|
/* Macros for stepping along the raw lines for super-resolution mode
|
|
They are very ugly because they handle boundary conditions at
|
|
the edges of the image. Yuck... */
|
|
|
|
#define PREV_RED(i) (((i)/3)*3)
|
|
#define NEXT_RED(i) (((i) >= BYTES_PER_RAW_LINE-3) ? BYTES_PER_RAW_LINE-3 : \
|
|
PREV_RED(i)+3)
|
|
#define PREV_GREEN(i) ((i)<1 ? 1 : PREV_RED((i)-1)+1)
|
|
#define NEXT_GREEN(i) ((i)<1 ? 1 : ((i) >= BYTES_PER_RAW_LINE-2) ? \
|
|
BYTES_PER_RAW_LINE-2 : PREV_GREEN(i)+3)
|
|
#define PREV_BLUE(i) ((i)<2 ? 2 : PREV_RED((i)-2)+2)
|
|
#define NEXT_BLUE(i) ((i)<2 ? 2 : ((i) >= BYTES_PER_RAW_LINE-1) ? \
|
|
BYTES_PER_RAW_LINE-1 : PREV_BLUE(i)+3)
|
|
|
|
#define ADVANCE_COEFF(i) (((i)==1) ? 3 : (i)-1);
|
|
|
|
/**********************************************************************
|
|
//%FUNCTION: DMCRead
|
|
//%ARGUMENTS:
|
|
// fd -- file descriptor
|
|
// typecode -- data type code
|
|
// qualifier -- data type qualifier
|
|
// maxlen -- tranfer length
|
|
// buf -- buffer to store data in
|
|
// len -- set to actual length of data
|
|
//%RETURNS:
|
|
// A SANE status code
|
|
//%DESCRIPTION:
|
|
// Reads the particular data selected by typecode and qualifier
|
|
// *********************************************************************/
|
|
static SANE_Status
|
|
DMCRead(int fd, unsigned int typecode, unsigned int qualifier,
|
|
SANE_Byte *buf, size_t maxlen, size_t *len)
|
|
{
|
|
uint8_t readCmd[10];
|
|
SANE_Status status;
|
|
|
|
readCmd[0] = 0x28;
|
|
readCmd[1] = 0;
|
|
readCmd[2] = typecode;
|
|
readCmd[3] = 0;
|
|
readCmd[4] = (qualifier >> 8) & 0xFF;
|
|
readCmd[5] = qualifier & 0xFF;
|
|
readCmd[6] = (maxlen >> 16) & 0xFF;
|
|
readCmd[7] = (maxlen >> 8) & 0xFF;
|
|
readCmd[8] = maxlen & 0xFF;
|
|
readCmd[9] = 0;
|
|
DBG(3, "DMCRead: typecode=%x, qualifier=%x, maxlen=%lu\n",
|
|
typecode, qualifier, (u_long) maxlen);
|
|
|
|
*len = maxlen;
|
|
status = sanei_scsi_cmd(fd, readCmd, sizeof(readCmd), buf, len);
|
|
DBG(3, "DMCRead: Read %lu bytes\n", (u_long) *len);
|
|
return status;
|
|
}
|
|
|
|
/**********************************************************************
|
|
//%FUNCTION: DMCWrite
|
|
//%ARGUMENTS:
|
|
// fd -- file descriptor
|
|
// typecode -- data type code
|
|
// qualifier -- data type qualifier
|
|
// maxlen -- tranfer length
|
|
// buf -- buffer to store data in
|
|
//%RETURNS:
|
|
// A SANE status code
|
|
//%DESCRIPTION:
|
|
// Writes the particular data selected by typecode and qualifier
|
|
// *********************************************************************/
|
|
static SANE_Status
|
|
DMCWrite(int fd, unsigned int typecode, unsigned int qualifier,
|
|
SANE_Byte *buf, size_t maxlen)
|
|
{
|
|
uint8_t *writeCmd;
|
|
SANE_Status status;
|
|
|
|
writeCmd = malloc(maxlen + 10);
|
|
if (!writeCmd) return SANE_STATUS_NO_MEM;
|
|
|
|
writeCmd[0] = 0x2A;
|
|
writeCmd[1] = 0;
|
|
writeCmd[2] = typecode;
|
|
writeCmd[3] = 0;
|
|
writeCmd[4] = (qualifier >> 8) & 0xFF;
|
|
writeCmd[5] = qualifier & 0xFF;
|
|
writeCmd[6] = (maxlen >> 16) & 0xFF;
|
|
writeCmd[7] = (maxlen >> 8) & 0xFF;
|
|
writeCmd[8] = maxlen & 0xFF;
|
|
writeCmd[9] = 0;
|
|
memcpy(writeCmd+10, buf, maxlen);
|
|
|
|
DBG(3, "DMCWrite: typecode=%x, qualifier=%x, maxlen=%lu\n",
|
|
typecode, qualifier, (u_long) maxlen);
|
|
|
|
status = sanei_scsi_cmd(fd, writeCmd, 10+maxlen, NULL, NULL);
|
|
free(writeCmd);
|
|
return status;
|
|
}
|
|
|
|
/**********************************************************************
|
|
//%FUNCTION: DMCAttach
|
|
//%ARGUMENTS:
|
|
// devname -- name of device file to open
|
|
// devp -- a DMC_Device structure which we fill in if it's not NULL.
|
|
//%RETURNS:
|
|
// SANE_STATUS_GOOD -- We have a Polaroid DMC attached and all looks good.
|
|
// SANE_STATUS_INVAL -- There's a problem.
|
|
//%DESCRIPTION:
|
|
// Verifies that a Polaroid DMC is attached. Sets up device options in
|
|
// DMC_Device structure.
|
|
// *********************************************************************/
|
|
#define INQ_LEN 255
|
|
static SANE_Status
|
|
DMCAttach(char const *devname, DMC_Device **devp)
|
|
{
|
|
DMC_Device *dev;
|
|
SANE_Status status;
|
|
int fd;
|
|
size_t size;
|
|
char result[INQ_LEN];
|
|
|
|
uint8_t exposureCalculationResults[16];
|
|
uint8_t userInterfaceSettings[16];
|
|
|
|
static uint8_t const inquiry[] =
|
|
{ 0x12, 0x00, 0x00, 0x00, INQ_LEN, 0x00 };
|
|
|
|
static uint8_t const test_unit_ready[] =
|
|
{ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
|
|
|
|
static uint8_t const no_viewfinder[] =
|
|
{ 0xC6, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
|
|
|
|
/* If we're already attached, do nothing */
|
|
|
|
for (dev = FirstDevice; dev; dev = dev->next) {
|
|
if (!strcmp(dev->sane.name, devname)) {
|
|
if (devp) *devp = dev;
|
|
return SANE_STATUS_GOOD;
|
|
}
|
|
}
|
|
|
|
DBG(3, "DMCAttach: opening `%s'\n", devname);
|
|
status = sanei_scsi_open(devname, &fd, 0, 0);
|
|
if (status != SANE_STATUS_GOOD) {
|
|
DBG(1, "DMCAttach: open failed (%s)\n", sane_strstatus(status));
|
|
return status;
|
|
}
|
|
|
|
DBG(3, "DMCAttach: sending INQUIRY\n");
|
|
size = sizeof(result);
|
|
status = sanei_scsi_cmd(fd, inquiry, sizeof(inquiry), result, &size);
|
|
if (status != SANE_STATUS_GOOD || size < 32) {
|
|
if (status == SANE_STATUS_GOOD) status = SANE_STATUS_INVAL;
|
|
DBG(1, "DMCAttach: inquiry failed (%s)\n", sane_strstatus(status));
|
|
sanei_scsi_close(fd);
|
|
return status;
|
|
}
|
|
|
|
/* Verify that we have a Polaroid DMC */
|
|
|
|
if (result[0] != 6 ||
|
|
strncmp(result+8, "POLAROID", 8) ||
|
|
strncmp(result+16, "DMC ", 8)) {
|
|
sanei_scsi_close(fd);
|
|
DBG(1, "DMCAttach: Device does not look like a Polaroid DMC\n");
|
|
return SANE_STATUS_INVAL;
|
|
}
|
|
|
|
DBG(3, "DMCAttach: sending TEST_UNIT_READY\n");
|
|
status = sanei_scsi_cmd(fd, test_unit_ready, sizeof(test_unit_ready),
|
|
NULL, NULL);
|
|
if (status != SANE_STATUS_GOOD) {
|
|
DBG(1, "DMCAttach: test unit ready failed (%s)\n",
|
|
sane_strstatus(status));
|
|
sanei_scsi_close(fd);
|
|
return status;
|
|
}
|
|
|
|
/* Read current ASA and shutter speed settings */
|
|
status = DMCRead(fd, 0x87, 0x4, exposureCalculationResults,
|
|
sizeof(exposureCalculationResults), &size);
|
|
if (status != SANE_STATUS_GOOD ||
|
|
size < sizeof(exposureCalculationResults)) {
|
|
DBG(1, "DMCAttach: Couldn't read exposure calculation results (%s)\n",
|
|
sane_strstatus(status));
|
|
sanei_scsi_close(fd);
|
|
if (status == SANE_STATUS_GOOD) status = SANE_STATUS_IO_ERROR;
|
|
return status;
|
|
}
|
|
|
|
/* Read current white balance settings */
|
|
status = DMCRead(fd, 0x82, 0x0, userInterfaceSettings,
|
|
sizeof(userInterfaceSettings), &size);
|
|
if (status != SANE_STATUS_GOOD ||
|
|
size < sizeof(userInterfaceSettings)) {
|
|
DBG(1, "DMCAttach: Couldn't read user interface settings (%s)\n",
|
|
sane_strstatus(status));
|
|
sanei_scsi_close(fd);
|
|
if (status == SANE_STATUS_GOOD) status = SANE_STATUS_IO_ERROR;
|
|
return status;
|
|
}
|
|
|
|
/* Shut off viewfinder mode */
|
|
status = sanei_scsi_cmd(fd, no_viewfinder, sizeof(no_viewfinder),
|
|
NULL, NULL);
|
|
if (status != SANE_STATUS_GOOD) {
|
|
sanei_scsi_close(fd);
|
|
return status;
|
|
}
|
|
sanei_scsi_close(fd);
|
|
|
|
DBG(3, "DMCAttach: Looks like we have a Polaroid DMC\n");
|
|
|
|
dev = malloc(sizeof(*dev));
|
|
if (!dev) return SANE_STATUS_NO_MEM;
|
|
memset(dev, 0, sizeof(*dev));
|
|
|
|
dev->sane.name = strdup(devname);
|
|
dev->sane.vendor = "Polaroid";
|
|
dev->sane.model = "DMC";
|
|
dev->sane.type = "still camera";
|
|
dev->next = FirstDevice;
|
|
dev->whiteBalance = userInterfaceSettings[5];
|
|
if (dev->whiteBalance > WHITE_BALANCE_FLUORESCENT) {
|
|
dev->whiteBalance = WHITE_BALANCE_FLUORESCENT;
|
|
}
|
|
|
|
/* Bright Eyes documentation gives these as shutter speed ranges (ms) */
|
|
/* dev->shutterSpeedRange.min = 8; */
|
|
/* dev->shutterSpeedRange.max = 320; */
|
|
|
|
/* User's manual says these are shutter speed ranges (ms) */
|
|
dev->shutterSpeedRange.min = 8;
|
|
dev->shutterSpeedRange.max = 1000;
|
|
dev->shutterSpeedRange.quant = 2;
|
|
dev->shutterSpeed =
|
|
(exposureCalculationResults[10] << 8) +
|
|
exposureCalculationResults[11];
|
|
|
|
/* Convert from ticks to ms */
|
|
dev->shutterSpeed = TICKS_TO_MS(dev->shutterSpeed);
|
|
|
|
dev->asa = exposureCalculationResults[13];
|
|
if (dev->asa > ASA_100) dev->asa = ASA_100;
|
|
dev->asa = ValidASAs[dev->asa + 1];
|
|
FirstDevice = dev;
|
|
NumDevices++;
|
|
if (devp) *devp = dev;
|
|
return SANE_STATUS_GOOD;
|
|
}
|
|
|
|
/**********************************************************************
|
|
//%FUNCTION: ValidateHandle
|
|
//%ARGUMENTS:
|
|
// handle -- a handle for an opened camera
|
|
//%RETURNS:
|
|
// A validated pointer to the camera or NULL if handle is not valid.
|
|
// *********************************************************************/
|
|
static DMC_Camera *
|
|
ValidateHandle(SANE_Handle handle)
|
|
{
|
|
DMC_Camera *c;
|
|
for (c = FirstHandle; c; c = c->next) {
|
|
if (c == handle) return c;
|
|
}
|
|
DBG(1, "ValidateHandle: invalid handle %p\n", handle);
|
|
return NULL;
|
|
}
|
|
|
|
/**********************************************************************
|
|
//%FUNCTION: DMCInitOptions
|
|
//%ARGUMENTS:
|
|
// c -- a DMC camera device
|
|
//%RETURNS:
|
|
// SANE_STATUS_GOOD -- OK
|
|
// SANE_STATUS_INVAL -- There's a problem.
|
|
//%DESCRIPTION:
|
|
// Initializes the options in the DMC_Camera structure
|
|
// *********************************************************************/
|
|
static SANE_Status
|
|
DMCInitOptions(DMC_Camera *c)
|
|
{
|
|
int i;
|
|
|
|
/* Image is initially 801x600 */
|
|
c->tl_x_range.min = 0;
|
|
c->tl_x_range.max = c->tl_x_range.min;
|
|
c->tl_x_range.quant = 1;
|
|
c->tl_y_range.min = 0;
|
|
c->tl_y_range.max = c->tl_y_range.min;
|
|
c->tl_y_range.quant = 1;
|
|
|
|
c->br_x_range.min = 800;
|
|
c->br_x_range.max = c->br_x_range.min;
|
|
c->br_x_range.quant = 1;
|
|
c->br_y_range.min = 599;
|
|
c->br_y_range.max = c->br_y_range.min;
|
|
c->br_y_range.quant = 1;
|
|
|
|
memset(c->opt, 0, sizeof(c->opt));
|
|
memset(c->val, 0, sizeof(c->val));
|
|
|
|
for (i=0; i<NUM_OPTIONS; i++) {
|
|
c->opt[i].type = SANE_TYPE_INT;
|
|
c->opt[i].size = sizeof(SANE_Word);
|
|
c->opt[i].cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT;
|
|
c->opt[i].unit = SANE_UNIT_NONE;
|
|
}
|
|
|
|
c->opt[OPT_NUM_OPTS].name = SANE_NAME_NUM_OPTIONS;
|
|
c->opt[OPT_NUM_OPTS].title = SANE_TITLE_NUM_OPTIONS;
|
|
c->opt[OPT_NUM_OPTS].desc = SANE_DESC_NUM_OPTIONS;
|
|
c->opt[OPT_NUM_OPTS].cap = SANE_CAP_SOFT_DETECT;
|
|
c->opt[OPT_NUM_OPTS].type = SANE_TYPE_INT;
|
|
c->opt[OPT_NUM_OPTS].constraint_type = SANE_CONSTRAINT_NONE;
|
|
c->val[OPT_NUM_OPTS].w = NUM_OPTIONS;
|
|
|
|
c->opt[OPT_GEOMETRY_GROUP].type = SANE_TYPE_GROUP;
|
|
c->opt[OPT_GEOMETRY_GROUP].name = "";
|
|
c->opt[OPT_GEOMETRY_GROUP].title = "Geometry";
|
|
c->opt[OPT_GEOMETRY_GROUP].desc = "";
|
|
c->opt[OPT_GEOMETRY_GROUP].cap = SANE_CAP_ADVANCED;
|
|
c->opt[OPT_GEOMETRY_GROUP].constraint_type = SANE_CONSTRAINT_NONE;
|
|
|
|
/* top-left x */
|
|
c->opt[OPT_TL_X].name = SANE_NAME_SCAN_TL_X;
|
|
c->opt[OPT_TL_X].title = SANE_TITLE_SCAN_TL_X;
|
|
c->opt[OPT_TL_X].desc = SANE_DESC_SCAN_TL_X;
|
|
c->opt[OPT_TL_X].type = SANE_TYPE_INT;
|
|
c->opt[OPT_TL_X].unit = SANE_UNIT_PIXEL;
|
|
c->opt[OPT_TL_X].constraint_type = SANE_CONSTRAINT_RANGE;
|
|
c->opt[OPT_TL_X].constraint.range = &c->tl_x_range;
|
|
c->val[OPT_TL_X].w = c->tl_x_range.min;
|
|
|
|
/* top-left y */
|
|
c->opt[OPT_TL_Y].name = SANE_NAME_SCAN_TL_Y;
|
|
c->opt[OPT_TL_Y].title = SANE_TITLE_SCAN_TL_Y;
|
|
c->opt[OPT_TL_Y].desc = SANE_DESC_SCAN_TL_Y;
|
|
c->opt[OPT_TL_Y].type = SANE_TYPE_INT;
|
|
c->opt[OPT_TL_Y].unit = SANE_UNIT_PIXEL;
|
|
c->opt[OPT_TL_Y].constraint_type = SANE_CONSTRAINT_RANGE;
|
|
c->opt[OPT_TL_Y].constraint.range = &c->tl_y_range;
|
|
c->val[OPT_TL_Y].w = c->tl_y_range.min;
|
|
|
|
/* bottom-right x */
|
|
c->opt[OPT_BR_X].name = SANE_NAME_SCAN_BR_X;
|
|
c->opt[OPT_BR_X].title = SANE_TITLE_SCAN_BR_X;
|
|
c->opt[OPT_BR_X].desc = SANE_DESC_SCAN_BR_X;
|
|
c->opt[OPT_BR_X].type = SANE_TYPE_INT;
|
|
c->opt[OPT_BR_X].unit = SANE_UNIT_PIXEL;
|
|
c->opt[OPT_BR_X].constraint_type = SANE_CONSTRAINT_RANGE;
|
|
c->opt[OPT_BR_X].constraint.range = &c->br_x_range;
|
|
c->val[OPT_BR_X].w = c->br_x_range.min;
|
|
|
|
/* bottom-right y */
|
|
c->opt[OPT_BR_Y].name = SANE_NAME_SCAN_BR_Y;
|
|
c->opt[OPT_BR_Y].title = SANE_TITLE_SCAN_BR_Y;
|
|
c->opt[OPT_BR_Y].desc = SANE_DESC_SCAN_BR_Y;
|
|
c->opt[OPT_BR_Y].type = SANE_TYPE_INT;
|
|
c->opt[OPT_BR_Y].unit = SANE_UNIT_PIXEL;
|
|
c->opt[OPT_BR_Y].constraint_type = SANE_CONSTRAINT_RANGE;
|
|
c->opt[OPT_BR_Y].constraint.range = &c->br_y_range;
|
|
c->val[OPT_BR_Y].w = c->br_y_range.min;
|
|
|
|
c->opt[OPT_MODE_GROUP].type = SANE_TYPE_GROUP;
|
|
c->opt[OPT_MODE_GROUP].name = "";
|
|
c->opt[OPT_MODE_GROUP].title = "Imaging Mode";
|
|
c->opt[OPT_MODE_GROUP].desc = "";
|
|
c->opt[OPT_MODE_GROUP].cap = SANE_CAP_ADVANCED;
|
|
c->opt[OPT_MODE_GROUP].constraint_type = SANE_CONSTRAINT_NONE;
|
|
|
|
c->opt[OPT_IMAGE_MODE].name = "imagemode";
|
|
c->opt[OPT_IMAGE_MODE].title = "Image Mode";
|
|
c->opt[OPT_IMAGE_MODE].desc = "Selects image mode: 800x600 full frame, 270x201 viewfinder mode, 1599x600 \"raw\" image, 80x60 thumbnail image or 1599x1200 \"super-resolution\" image";
|
|
c->opt[OPT_IMAGE_MODE].type = SANE_TYPE_STRING;
|
|
c->opt[OPT_IMAGE_MODE].constraint_type = SANE_CONSTRAINT_STRING_LIST;
|
|
c->opt[OPT_IMAGE_MODE].constraint.string_list = ValidModes;
|
|
c->opt[OPT_IMAGE_MODE].size = 16;
|
|
c->val[OPT_IMAGE_MODE].s = "Full frame";
|
|
|
|
c->opt[OPT_ASA].name = "asa";
|
|
c->opt[OPT_ASA].title = "ASA Setting";
|
|
c->opt[OPT_ASA].desc = "Equivalent ASA setting";
|
|
c->opt[OPT_ASA].constraint_type = SANE_CONSTRAINT_WORD_LIST;
|
|
c->opt[OPT_ASA].constraint.word_list = ValidASAs;
|
|
c->val[OPT_ASA].w = c->hw->asa;
|
|
|
|
c->opt[OPT_SHUTTER_SPEED].name = "shutterspeed";
|
|
c->opt[OPT_SHUTTER_SPEED].title = "Shutter Speed (ms)";
|
|
c->opt[OPT_SHUTTER_SPEED].desc = "Shutter Speed in milliseconds";
|
|
c->opt[OPT_SHUTTER_SPEED].constraint_type = SANE_CONSTRAINT_RANGE;
|
|
c->opt[OPT_SHUTTER_SPEED].constraint.range = &c->hw->shutterSpeedRange;
|
|
c->val[OPT_SHUTTER_SPEED].w = c->hw->shutterSpeed;
|
|
|
|
c->opt[OPT_WHITE_BALANCE].name = "whitebalance";
|
|
c->opt[OPT_WHITE_BALANCE].title = "White Balance";
|
|
c->opt[OPT_WHITE_BALANCE].desc = "Selects white balance";
|
|
c->opt[OPT_WHITE_BALANCE].type = SANE_TYPE_STRING;
|
|
c->opt[OPT_WHITE_BALANCE].constraint_type = SANE_CONSTRAINT_STRING_LIST;
|
|
c->opt[OPT_WHITE_BALANCE].constraint.string_list = ValidBalances;
|
|
c->opt[OPT_WHITE_BALANCE].size = 16;
|
|
c->val[OPT_WHITE_BALANCE].s = (SANE_String) ValidBalances[c->hw->whiteBalance];
|
|
|
|
return SANE_STATUS_GOOD;
|
|
}
|
|
|
|
/**********************************************************************
|
|
//%FUNCTION: DMCSetMode
|
|
//%ARGUMENTS:
|
|
// c -- a DMC camera device
|
|
// mode -- Imaging mode
|
|
//%RETURNS:
|
|
// SANE_STATUS_GOOD -- OK
|
|
// SANE_STATUS_INVAL -- There's a problem.
|
|
//%DESCRIPTION:
|
|
// Sets the camera's imaging mode.
|
|
// *********************************************************************/
|
|
static SANE_Status
|
|
DMCSetMode(DMC_Camera *c, int mode)
|
|
{
|
|
switch(mode) {
|
|
case IMAGE_MFI:
|
|
c->tl_x_range.min = 0;
|
|
c->tl_x_range.max = c->tl_x_range.max;
|
|
c->tl_y_range.min = 0;
|
|
c->tl_y_range.max = c->tl_y_range.max;
|
|
c->br_x_range.min = 800;
|
|
c->br_x_range.max = c->br_x_range.max;
|
|
c->br_y_range.min = 599;
|
|
c->br_y_range.max = c->br_y_range.max;
|
|
break;
|
|
case IMAGE_VIEWFINDER:
|
|
c->tl_x_range.min = 0;
|
|
c->tl_x_range.max = c->tl_x_range.max;
|
|
c->tl_y_range.min = 0;
|
|
c->tl_y_range.max = c->tl_y_range.max;
|
|
c->br_x_range.min = 269;
|
|
c->br_x_range.max = c->br_x_range.max;
|
|
c->br_y_range.min = 200;
|
|
c->br_y_range.max = c->br_y_range.max;
|
|
break;
|
|
case IMAGE_RAW:
|
|
c->tl_x_range.min = 0;
|
|
c->tl_x_range.max = c->tl_x_range.max;
|
|
c->tl_y_range.min = 0;
|
|
c->tl_y_range.max = c->tl_y_range.max;
|
|
c->br_x_range.min = 1598;
|
|
c->br_x_range.max = c->br_x_range.max;
|
|
c->br_y_range.min = 599;
|
|
c->br_y_range.max = c->br_y_range.max;
|
|
break;
|
|
case IMAGE_THUMB:
|
|
c->tl_x_range.min = 0;
|
|
c->tl_x_range.max = c->tl_x_range.max;
|
|
c->tl_y_range.min = 0;
|
|
c->tl_y_range.max = c->tl_y_range.max;
|
|
c->br_x_range.min = 79;
|
|
c->br_x_range.max = c->br_x_range.max;
|
|
c->br_y_range.min = 59;
|
|
c->br_y_range.max = c->br_y_range.max;
|
|
break;
|
|
case IMAGE_SUPER_RES:
|
|
c->tl_x_range.min = 0;
|
|
c->tl_x_range.max = c->tl_x_range.max;
|
|
c->tl_y_range.min = 0;
|
|
c->tl_y_range.max = c->tl_y_range.max;
|
|
c->br_x_range.min = 1598;
|
|
c->br_x_range.max = c->br_x_range.max;
|
|
c->br_y_range.min = 1199;
|
|
c->br_y_range.max = c->br_y_range.max;
|
|
break;
|
|
default:
|
|
return SANE_STATUS_INVAL;
|
|
}
|
|
c->imageMode = mode;
|
|
c->val[OPT_TL_X].w = c->tl_x_range.min;
|
|
c->val[OPT_TL_Y].w = c->tl_y_range.min;
|
|
c->val[OPT_BR_X].w = c->br_x_range.min;
|
|
c->val[OPT_BR_Y].w = c->br_y_range.min;
|
|
return SANE_STATUS_GOOD;
|
|
}
|
|
|
|
/**********************************************************************
|
|
//%FUNCTION: DMCCancel
|
|
//%ARGUMENTS:
|
|
// c -- a DMC camera device
|
|
//%RETURNS:
|
|
// SANE_STATUS_CANCELLED
|
|
//%DESCRIPTION:
|
|
// Cancels DMC image acquisition
|
|
// *********************************************************************/
|
|
static SANE_Status
|
|
DMCCancel(DMC_Camera *c)
|
|
{
|
|
if (c->fd >= 0) {
|
|
sanei_scsi_close(c->fd);
|
|
c->fd = -1;
|
|
}
|
|
return SANE_STATUS_CANCELLED;
|
|
}
|
|
|
|
/**********************************************************************
|
|
//%FUNCTION: DMCSetASA
|
|
//%ARGUMENTS:
|
|
// fd -- SCSI file descriptor
|
|
// asa -- the ASA to set
|
|
//%RETURNS:
|
|
// A sane status value
|
|
//%DESCRIPTION:
|
|
// Sets the equivalent ASA setting of the camera.
|
|
// *********************************************************************/
|
|
static SANE_Status
|
|
DMCSetASA(int fd, unsigned int asa)
|
|
{
|
|
uint8_t exposureCalculationResults[16];
|
|
SANE_Status status;
|
|
size_t len;
|
|
int i;
|
|
|
|
DBG(3, "DMCSetAsa: %d\n", asa);
|
|
for (i=1; i<=ASA_100+1; i++) {
|
|
if (asa == (unsigned int) ValidASAs[i]) break;
|
|
}
|
|
|
|
if (i > ASA_100+1) return SANE_STATUS_INVAL;
|
|
|
|
status = DMCRead(fd, 0x87, 0x4, exposureCalculationResults,
|
|
sizeof(exposureCalculationResults), &len);
|
|
if (status != SANE_STATUS_GOOD) return status;
|
|
if (len < sizeof(exposureCalculationResults)) return SANE_STATUS_IO_ERROR;
|
|
|
|
exposureCalculationResults[13] = (uint8_t) i - 1;
|
|
|
|
return DMCWrite(fd, 0x87, 0x4, exposureCalculationResults,
|
|
sizeof(exposureCalculationResults));
|
|
}
|
|
|
|
/**********************************************************************
|
|
//%FUNCTION: DMCSetWhiteBalance
|
|
//%ARGUMENTS:
|
|
// fd -- SCSI file descriptor
|
|
// mode -- white balance mode
|
|
//%RETURNS:
|
|
// A sane status value
|
|
//%DESCRIPTION:
|
|
// Sets the equivalent ASA setting of the camera.
|
|
// *********************************************************************/
|
|
static SANE_Status
|
|
DMCSetWhiteBalance(int fd, int mode)
|
|
{
|
|
uint8_t userInterfaceSettings[16];
|
|
SANE_Status status;
|
|
size_t len;
|
|
|
|
DBG(3, "DMCSetWhiteBalance: %d\n", mode);
|
|
status = DMCRead(fd, 0x82, 0x0, userInterfaceSettings,
|
|
sizeof(userInterfaceSettings), &len);
|
|
if (status != SANE_STATUS_GOOD) return status;
|
|
if (len < sizeof(userInterfaceSettings)) return SANE_STATUS_IO_ERROR;
|
|
|
|
userInterfaceSettings[5] = (uint8_t) mode;
|
|
|
|
return DMCWrite(fd, 0x82, 0x0, userInterfaceSettings,
|
|
sizeof(userInterfaceSettings));
|
|
}
|
|
|
|
/**********************************************************************
|
|
//%FUNCTION: DMCSetShutterSpeed
|
|
//%ARGUMENTS:
|
|
// fd -- SCSI file descriptor
|
|
// speed -- shutter speed in ms
|
|
//%RETURNS:
|
|
// A sane status value
|
|
//%DESCRIPTION:
|
|
// Sets the shutter speed of the camera
|
|
// *********************************************************************/
|
|
static SANE_Status
|
|
DMCSetShutterSpeed(int fd, unsigned int speed)
|
|
{
|
|
uint8_t exposureCalculationResults[16];
|
|
SANE_Status status;
|
|
size_t len;
|
|
|
|
DBG(3, "DMCSetShutterSpeed: %u\n", speed);
|
|
/* Convert from ms to ticks */
|
|
speed = MS_TO_TICKS(speed);
|
|
|
|
status = DMCRead(fd, 0x87, 0x4, exposureCalculationResults,
|
|
sizeof(exposureCalculationResults), &len);
|
|
if (status != SANE_STATUS_GOOD) return status;
|
|
if (len < sizeof(exposureCalculationResults)) return SANE_STATUS_IO_ERROR;
|
|
|
|
exposureCalculationResults[10] = (speed >> 8) & 0xFF;
|
|
exposureCalculationResults[11] = speed & 0xFF;
|
|
|
|
return DMCWrite(fd, 0x87, 0x4, exposureCalculationResults,
|
|
sizeof(exposureCalculationResults));
|
|
}
|
|
|
|
/**********************************************************************
|
|
//%FUNCTION: DMCReadTwoSuperResolutionLines
|
|
//%ARGUMENTS:
|
|
// c -- DMC Camera
|
|
// buf -- where to put output.
|
|
// lastLine -- if true, these are the last two lines in the super-resolution
|
|
// image to read.
|
|
//%RETURNS:
|
|
// Nothing
|
|
//%DESCRIPTION:
|
|
// Reads a single "raw" line from the camera (if needed) and constructs
|
|
// two "super-resolution" output lines in "buf"
|
|
// *********************************************************************/
|
|
static SANE_Status
|
|
DMCReadTwoSuperResolutionLines(DMC_Camera *c, SANE_Byte *buf, int lastLine)
|
|
{
|
|
SANE_Status status;
|
|
size_t len;
|
|
|
|
SANE_Byte *output, *prev;
|
|
int redCoeff, greenCoeff, blueCoeff;
|
|
int red, green, blue;
|
|
int i;
|
|
|
|
if (c->nextRawLineValid) {
|
|
memcpy(c->currentRawLine, c->nextRawLine, BYTES_PER_RAW_LINE);
|
|
} else {
|
|
status = DMCRead(c->fd, 0x00, IMAGE_RAW,
|
|
c->currentRawLine, BYTES_PER_RAW_LINE, &len);
|
|
if (status != SANE_STATUS_GOOD) return status;
|
|
}
|
|
if (!lastLine) {
|
|
status = DMCRead(c->fd, 0x00, IMAGE_RAW,
|
|
c->nextRawLine, BYTES_PER_RAW_LINE, &len);
|
|
if (status != SANE_STATUS_GOOD) return status;
|
|
c->nextRawLineValid = 1;
|
|
}
|
|
|
|
redCoeff = 3;
|
|
greenCoeff = 1;
|
|
blueCoeff = 2;
|
|
|
|
/* Do the first super-resolution line */
|
|
output = buf;
|
|
for (i=0; i<BYTES_PER_RAW_LINE; i++) {
|
|
red = redCoeff * c->currentRawLine[PREV_RED(i)] +
|
|
(3-redCoeff) * c->currentRawLine[NEXT_RED(i)];
|
|
green = greenCoeff * c->currentRawLine[PREV_GREEN(i)] +
|
|
(3-greenCoeff) * c->currentRawLine[NEXT_GREEN(i)];
|
|
blue = blueCoeff * c->currentRawLine[PREV_BLUE(i)] +
|
|
(3-blueCoeff) * c->currentRawLine[NEXT_BLUE(i)];
|
|
*output++ = red/3;
|
|
*output++ = green/3;
|
|
*output++ = blue/3;
|
|
redCoeff = ADVANCE_COEFF(redCoeff);
|
|
greenCoeff = ADVANCE_COEFF(greenCoeff);
|
|
blueCoeff = ADVANCE_COEFF(blueCoeff);
|
|
}
|
|
|
|
/* Do the next super-resolution line and interpolate vertically */
|
|
if (lastLine) {
|
|
memcpy(buf+BYTES_PER_RAW_LINE*3, buf, BYTES_PER_RAW_LINE*3);
|
|
return SANE_STATUS_GOOD;
|
|
}
|
|
redCoeff = 3;
|
|
greenCoeff = 1;
|
|
blueCoeff = 2;
|
|
|
|
prev = buf;
|
|
for (i=0; i<BYTES_PER_RAW_LINE; i++) {
|
|
red = redCoeff * c->nextRawLine[PREV_RED(i)] +
|
|
(3-redCoeff) * c->nextRawLine[NEXT_RED(i)];
|
|
green = greenCoeff * c->nextRawLine[PREV_GREEN(i)] +
|
|
(3-greenCoeff) * c->nextRawLine[NEXT_GREEN(i)];
|
|
blue = blueCoeff * c->nextRawLine[PREV_BLUE(i)] +
|
|
(3-blueCoeff) * c->nextRawLine[NEXT_BLUE(i)];
|
|
*output++ = (red/3 + *prev++) / 2;
|
|
*output++ = (green/3 + *prev++) / 2;
|
|
*output++ = (blue/3 + *prev++) / 2;
|
|
redCoeff = ADVANCE_COEFF(redCoeff);
|
|
greenCoeff = ADVANCE_COEFF(greenCoeff);
|
|
blueCoeff = ADVANCE_COEFF(blueCoeff);
|
|
}
|
|
return SANE_STATUS_GOOD;
|
|
}
|
|
|
|
/***********************************************************************
|
|
//%FUNCTION: attach_one (static function)
|
|
//%ARGUMENTS:
|
|
// dev -- device to attach
|
|
//%RETURNS:
|
|
// SANE_STATUS_GOOD
|
|
//%DESCRIPTION:
|
|
// tries to attach a device found by sanei_config_attach_matching_devices
|
|
// *********************************************************************/
|
|
static SANE_Status
|
|
attach_one (const char *dev)
|
|
{
|
|
DMCAttach (dev, 0);
|
|
return SANE_STATUS_GOOD;
|
|
}
|
|
|
|
/**********************************************************************
|
|
//%FUNCTION: sane_init
|
|
//%ARGUMENTS:
|
|
// version_code -- pointer to where we stick our version code
|
|
// authorize -- authorization function
|
|
//%RETURNS:
|
|
// A sane status value
|
|
//%DESCRIPTION:
|
|
// Initializes DMC sane system.
|
|
// *********************************************************************/
|
|
SANE_Status
|
|
sane_init(SANE_Int *version_code, SANE_Auth_Callback authorize)
|
|
{
|
|
char dev_name[PATH_MAX];
|
|
size_t len;
|
|
FILE *fp;
|
|
|
|
authorize = authorize;
|
|
|
|
DBG_INIT();
|
|
if (version_code) {
|
|
*version_code = SANE_VERSION_CODE (SANE_CURRENT_MAJOR, V_MINOR, 0);
|
|
}
|
|
|
|
fp = sanei_config_open(DMC_CONFIG_FILE);
|
|
if (!fp) {
|
|
/* default to /dev/camera instead of insisting on config file */
|
|
if (DMCAttach ("/dev/camera", NULL) != SANE_STATUS_GOOD) {
|
|
/* OK, try /dev/scanner */
|
|
DMCAttach("/dev/scanner", NULL);
|
|
}
|
|
return SANE_STATUS_GOOD;
|
|
}
|
|
|
|
while (sanei_config_read (dev_name, sizeof (dev_name), fp)) {
|
|
if (dev_name[0] == '#') { /* ignore line comments */
|
|
continue;
|
|
}
|
|
len = strlen (dev_name);
|
|
|
|
if (!len) continue; /* ignore empty lines */
|
|
|
|
sanei_config_attach_matching_devices(dev_name, attach_one);
|
|
}
|
|
fclose (fp);
|
|
return SANE_STATUS_GOOD;
|
|
}
|
|
|
|
/**********************************************************************
|
|
//%FUNCTION: sane_exit
|
|
//%ARGUMENTS:
|
|
// None
|
|
//%RETURNS:
|
|
// Nothing
|
|
//%DESCRIPTION:
|
|
// Cleans up all the SANE information
|
|
// *********************************************************************/
|
|
void
|
|
sane_exit(void)
|
|
{
|
|
DMC_Device *dev, *next;
|
|
|
|
/* Close all handles */
|
|
while(FirstHandle) {
|
|
sane_close(FirstHandle);
|
|
}
|
|
|
|
/* Free all devices */
|
|
dev = FirstDevice;
|
|
while(dev) {
|
|
next = dev->next;
|
|
free((char *) dev->sane.model);
|
|
free(dev);
|
|
dev = next;
|
|
}
|
|
}
|
|
|
|
/**********************************************************************
|
|
//%FUNCTION: sane_get_devices
|
|
//%ARGUMENTS:
|
|
// device_list -- set to allocated list of devices
|
|
// local_only -- ignored
|
|
//%RETURNS:
|
|
// A SANE status
|
|
//%DESCRIPTION:
|
|
// Returns a list of all known DMC devices
|
|
// *********************************************************************/
|
|
SANE_Status
|
|
sane_get_devices(SANE_Device const ***device_list, SANE_Bool local_only)
|
|
{
|
|
static SANE_Device const **devlist = 0;
|
|
DMC_Device *dev;
|
|
int i = 0;
|
|
|
|
local_only = local_only;
|
|
|
|
if (devlist) free(devlist);
|
|
devlist = malloc((NumDevices+1) * sizeof(devlist[0]));
|
|
if (!devlist) return SANE_STATUS_NO_MEM;
|
|
|
|
for (dev=FirstDevice; dev; dev = dev->next) {
|
|
devlist[i++] = &dev->sane;
|
|
}
|
|
devlist[i] = NULL;
|
|
|
|
if (device_list) *device_list = devlist;
|
|
|
|
return SANE_STATUS_GOOD;
|
|
}
|
|
|
|
/**********************************************************************
|
|
//%FUNCTION: sane_open
|
|
//%ARGUMENTS:
|
|
// name -- name of device to open
|
|
// handle -- set to a handle for the opened device
|
|
//%RETURNS:
|
|
// A SANE status
|
|
//%DESCRIPTION:
|
|
// Opens a DMC camera device
|
|
// *********************************************************************/
|
|
SANE_Status
|
|
sane_open(SANE_String_Const name, SANE_Handle *handle)
|
|
{
|
|
SANE_Status status;
|
|
DMC_Device *dev;
|
|
DMC_Camera *c;
|
|
|
|
/* If we're given a device name, search for it */
|
|
if (*name) {
|
|
for (dev = FirstDevice; dev; dev = dev->next) {
|
|
if (!strcmp(dev->sane.name, name)) {
|
|
break;
|
|
}
|
|
}
|
|
if (!dev) {
|
|
status = DMCAttach(name, &dev);
|
|
if (status != SANE_STATUS_GOOD) return status;
|
|
}
|
|
} else {
|
|
dev = FirstDevice;
|
|
}
|
|
|
|
if (!dev) return SANE_STATUS_INVAL;
|
|
|
|
c = malloc(sizeof(*c));
|
|
if (!c) return SANE_STATUS_NO_MEM;
|
|
|
|
memset(c, 0, sizeof(*c));
|
|
|
|
c->fd = -1;
|
|
c->hw = dev;
|
|
c->readBuffer = NULL;
|
|
c->readPtr = NULL;
|
|
c->imageMode = IMAGE_MFI;
|
|
c->inViewfinderMode = 0;
|
|
c->nextRawLineValid = 0;
|
|
|
|
DMCInitOptions(c);
|
|
|
|
c->next = FirstHandle;
|
|
FirstHandle = c;
|
|
if (handle) *handle = c;
|
|
return SANE_STATUS_GOOD;
|
|
}
|
|
|
|
/**********************************************************************
|
|
//%FUNCTION: sane_close
|
|
//%ARGUMENTS:
|
|
// handle -- handle of device to close
|
|
//%RETURNS:
|
|
// A SANE status
|
|
//%DESCRIPTION:
|
|
// Closes a DMC camera device
|
|
// *********************************************************************/
|
|
void
|
|
sane_close(SANE_Handle handle)
|
|
{
|
|
DMC_Camera *prev, *c;
|
|
prev = NULL;
|
|
for (c = FirstHandle; c; c = c->next) {
|
|
if (c == handle) break;
|
|
prev = c;
|
|
}
|
|
if (!c) {
|
|
DBG(1, "close: invalid handle %p\n", handle);
|
|
return;
|
|
}
|
|
DMCCancel(c);
|
|
|
|
if (prev) prev->next = c->next;
|
|
else FirstHandle = c->next;
|
|
|
|
if (c->readBuffer) {
|
|
free(c->readBuffer);
|
|
}
|
|
free(c);
|
|
}
|
|
|
|
/**********************************************************************
|
|
//%FUNCTION: sane_get_option_descriptor
|
|
//%ARGUMENTS:
|
|
// handle -- handle of device
|
|
// option -- option number to retrieve
|
|
//%RETURNS:
|
|
// An option descriptor or NULL on error
|
|
// *********************************************************************/
|
|
SANE_Option_Descriptor const *
|
|
sane_get_option_descriptor(SANE_Handle handle, SANE_Int option)
|
|
{
|
|
DMC_Camera *c = ValidateHandle(handle);
|
|
if (!c) return NULL;
|
|
|
|
if ((unsigned) option >= NUM_OPTIONS) return NULL;
|
|
return c->opt + option;
|
|
}
|
|
|
|
/**********************************************************************
|
|
//%FUNCTION: sane_control_option
|
|
//%ARGUMENTS:
|
|
// handle -- handle of device
|
|
// option -- option number to retrieve
|
|
// action -- what to do with the option
|
|
// val -- value to set option to
|
|
// info -- returned info flags
|
|
//%RETURNS:
|
|
// SANE status
|
|
//%DESCRIPTION:
|
|
// Sets or queries option values
|
|
// *********************************************************************/
|
|
SANE_Status
|
|
sane_control_option(SANE_Handle handle, SANE_Int option,
|
|
SANE_Action action, void *val, SANE_Int *info)
|
|
{
|
|
DMC_Camera *c;
|
|
SANE_Word cap;
|
|
SANE_Status status;
|
|
int i;
|
|
|
|
if (info) *info = 0;
|
|
|
|
c = ValidateHandle(handle);
|
|
if (!c) return SANE_STATUS_INVAL;
|
|
|
|
if (c->fd >= 0) return SANE_STATUS_DEVICE_BUSY;
|
|
|
|
if (option >= NUM_OPTIONS) return SANE_STATUS_INVAL;
|
|
|
|
cap = c->opt[option].cap;
|
|
if (!SANE_OPTION_IS_ACTIVE(cap)) return SANE_STATUS_INVAL;
|
|
|
|
if (action == SANE_ACTION_GET_VALUE) {
|
|
switch(c->opt[option].type) {
|
|
case SANE_TYPE_INT:
|
|
* (SANE_Int *) val = c->val[option].w;
|
|
return SANE_STATUS_GOOD;
|
|
|
|
case SANE_TYPE_STRING:
|
|
strcpy(val, c->val[option].s);
|
|
return SANE_STATUS_GOOD;
|
|
|
|
default:
|
|
DBG(3, "impossible option type!\n");
|
|
return SANE_STATUS_INVAL;
|
|
}
|
|
}
|
|
|
|
if (action == SANE_ACTION_SET_AUTO) {
|
|
return SANE_STATUS_UNSUPPORTED;
|
|
}
|
|
|
|
switch(option) {
|
|
case OPT_IMAGE_MODE:
|
|
for (i=0; i<NUM_IMAGE_MODES; i++) {
|
|
if (!strcmp(val, ValidModes[i])) {
|
|
status = DMCSetMode(c, i);
|
|
c->val[OPT_IMAGE_MODE].s = (SANE_String) ValidModes[i];
|
|
if (info) *info |= SANE_INFO_RELOAD_PARAMS | SANE_INFO_RELOAD_OPTIONS;
|
|
return SANE_STATUS_GOOD;
|
|
}
|
|
}
|
|
break;
|
|
case OPT_WHITE_BALANCE:
|
|
for (i=0; i<=WHITE_BALANCE_FLUORESCENT; i++) {
|
|
if (!strcmp(val, ValidBalances[i])) {
|
|
c->val[OPT_WHITE_BALANCE].s = (SANE_String) ValidBalances[i];
|
|
return SANE_STATUS_GOOD;
|
|
}
|
|
}
|
|
break;
|
|
case OPT_ASA:
|
|
for (i=1; i<= ASA_100+1; i++) {
|
|
if (* ((SANE_Int *) val) == ValidASAs[i]) {
|
|
c->val[OPT_ASA].w = ValidASAs[i];
|
|
return SANE_STATUS_GOOD;
|
|
}
|
|
}
|
|
break;
|
|
case OPT_SHUTTER_SPEED:
|
|
if (* (SANE_Int *) val < c->hw->shutterSpeedRange.min ||
|
|
* (SANE_Int *) val > c->hw->shutterSpeedRange.max) {
|
|
return SANE_STATUS_INVAL;
|
|
}
|
|
c->val[OPT_SHUTTER_SPEED].w = * (SANE_Int *) val;
|
|
/* Do any roundoff */
|
|
c->val[OPT_SHUTTER_SPEED].w =
|
|
TICKS_TO_MS(MS_TO_TICKS(c->val[OPT_SHUTTER_SPEED].w));
|
|
if (c->val[OPT_SHUTTER_SPEED].w != * (SANE_Int *) val) {
|
|
if (info) *info |= SANE_INFO_INEXACT;
|
|
}
|
|
|
|
return SANE_STATUS_GOOD;
|
|
|
|
default:
|
|
/* Should really be INVAL, but just bit-bucket set requests... */
|
|
return SANE_STATUS_GOOD;
|
|
}
|
|
|
|
return SANE_STATUS_INVAL;
|
|
}
|
|
|
|
/**********************************************************************
|
|
//%FUNCTION: sane_get_parameters
|
|
//%ARGUMENTS:
|
|
// handle -- handle of device
|
|
// params -- set to device parameters
|
|
//%RETURNS:
|
|
// SANE status
|
|
//%DESCRIPTION:
|
|
// Returns parameters for current or next image.
|
|
// *********************************************************************/
|
|
SANE_Status
|
|
sane_get_parameters(SANE_Handle handle, SANE_Parameters *params)
|
|
{
|
|
DMC_Camera *c = ValidateHandle(handle);
|
|
if (!c) return SANE_STATUS_INVAL;
|
|
|
|
if (c->fd < 0) {
|
|
int width, height;
|
|
memset(&c->params, 0, sizeof(c->params));
|
|
|
|
width = c->val[OPT_BR_X].w - c->val[OPT_TL_X].w;
|
|
height = c->val[OPT_BR_Y].w - c->val[OPT_TL_Y].w;
|
|
c->params.pixels_per_line = width + 1;
|
|
c->params.lines = height+1;
|
|
c->params.depth = 8;
|
|
c->params.last_frame = SANE_TRUE;
|
|
switch(c->imageMode) {
|
|
case IMAGE_SUPER_RES:
|
|
case IMAGE_MFI:
|
|
case IMAGE_THUMB:
|
|
c->params.format = SANE_FRAME_RGB;
|
|
c->params.bytes_per_line = c->params.pixels_per_line * 3;
|
|
break;
|
|
case IMAGE_RAW:
|
|
case IMAGE_VIEWFINDER:
|
|
c->params.format = SANE_FRAME_GRAY;
|
|
c->params.bytes_per_line = c->params.pixels_per_line;
|
|
break;
|
|
}
|
|
}
|
|
if (params) *params = c->params;
|
|
return SANE_STATUS_GOOD;
|
|
}
|
|
|
|
/**********************************************************************
|
|
//%FUNCTION: sane_start
|
|
//%ARGUMENTS:
|
|
// handle -- handle of device
|
|
//%RETURNS:
|
|
// SANE status
|
|
//%DESCRIPTION:
|
|
// Starts acquisition
|
|
// *********************************************************************/
|
|
SANE_Status
|
|
sane_start(SANE_Handle handle)
|
|
{
|
|
static uint8_t const acquire[] =
|
|
{ 0xC1, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
|
|
|
|
static uint8_t const viewfinder[] =
|
|
{ 0xCB, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
|
|
|
|
static uint8_t const no_viewfinder[] =
|
|
{ 0xC6, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
|
|
|
|
DMC_Camera *c = ValidateHandle(handle);
|
|
SANE_Status status;
|
|
int i;
|
|
|
|
if (!c) return SANE_STATUS_INVAL;
|
|
|
|
/* If we're already open, barf -- not sure this is the best status */
|
|
if (c->fd >= 0) return SANE_STATUS_DEVICE_BUSY;
|
|
|
|
/* Get rid of old read buffers */
|
|
if (c->readBuffer) {
|
|
free(c->readBuffer);
|
|
c->readBuffer = NULL;
|
|
c->readPtr = NULL;
|
|
}
|
|
|
|
c->nextRawLineValid = 0;
|
|
|
|
/* Refresh parameter list */
|
|
status = sane_get_parameters(c, NULL);
|
|
if (status != SANE_STATUS_GOOD) return status;
|
|
|
|
status = sanei_scsi_open(c->hw->sane.name, &c->fd, NULL, NULL);
|
|
if (status != SANE_STATUS_GOOD) {
|
|
c->fd = -1;
|
|
DBG(1, "DMC: Open of `%s' failed: %s\n",
|
|
c->hw->sane.name, sane_strstatus(status));
|
|
return status;
|
|
}
|
|
|
|
/* Set ASA and shutter speed if they're no longer current */
|
|
if (c->val[OPT_ASA].w != c->hw->asa) {
|
|
status = DMCSetASA(c->fd, c->val[OPT_ASA].w);
|
|
if (status != SANE_STATUS_GOOD) {
|
|
DMCCancel(c);
|
|
return status;
|
|
}
|
|
c->hw->asa = c->val[OPT_ASA].w;
|
|
}
|
|
|
|
if ((unsigned int) c->val[OPT_SHUTTER_SPEED].w != c->hw->shutterSpeed) {
|
|
status = DMCSetShutterSpeed(c->fd, c->val[OPT_SHUTTER_SPEED].w);
|
|
if (status != SANE_STATUS_GOOD) {
|
|
DMCCancel(c);
|
|
return status;
|
|
}
|
|
c->hw->shutterSpeed = c->val[OPT_SHUTTER_SPEED].w;
|
|
}
|
|
|
|
/* Set white balance mode if needed */
|
|
for (i=0; i<=WHITE_BALANCE_FLUORESCENT; i++) {
|
|
if (!strcmp(ValidBalances[i], c->val[OPT_WHITE_BALANCE].s)) {
|
|
if (i != c->hw->whiteBalance) {
|
|
status = DMCSetWhiteBalance(c->fd, i);
|
|
if (status != SANE_STATUS_GOOD) {
|
|
DMCCancel(c);
|
|
return status;
|
|
}
|
|
c->hw->whiteBalance = i;
|
|
}
|
|
}
|
|
}
|
|
|
|
/* Flip into viewfinder mode if needed */
|
|
if (c->imageMode == IMAGE_VIEWFINDER && !c->inViewfinderMode) {
|
|
status = sanei_scsi_cmd(c->fd, viewfinder, sizeof(viewfinder),
|
|
NULL, NULL);
|
|
if (status != SANE_STATUS_GOOD) {
|
|
DMCCancel(c);
|
|
return status;
|
|
}
|
|
c->inViewfinderMode = 1;
|
|
}
|
|
|
|
/* Flip out of viewfinder mode if needed */
|
|
if (c->imageMode != IMAGE_VIEWFINDER && c->inViewfinderMode) {
|
|
status = sanei_scsi_cmd(c->fd, no_viewfinder, sizeof(no_viewfinder),
|
|
NULL, NULL);
|
|
if (status != SANE_STATUS_GOOD) {
|
|
DMCCancel(c);
|
|
return status;
|
|
}
|
|
c->inViewfinderMode = 0;
|
|
}
|
|
|
|
|
|
status = sanei_scsi_cmd(c->fd, acquire, sizeof(acquire), NULL, NULL);
|
|
if (status != SANE_STATUS_GOOD) {
|
|
DMCCancel(c);
|
|
return status;
|
|
}
|
|
c->bytes_to_read = c->params.bytes_per_line * c->params.lines;
|
|
return SANE_STATUS_GOOD;
|
|
}
|
|
|
|
/**********************************************************************
|
|
//%FUNCTION: sane_read
|
|
//%ARGUMENTS:
|
|
// handle -- handle of device
|
|
// buf -- destination for data
|
|
// max_len -- maximum amount of data to store
|
|
// len -- set to actual amount of data stored.
|
|
//%RETURNS:
|
|
// SANE status
|
|
//%DESCRIPTION:
|
|
// Reads image data from the camera
|
|
// *********************************************************************/
|
|
SANE_Status
|
|
sane_read(SANE_Handle handle, SANE_Byte *buf, SANE_Int max_len, SANE_Int *len)
|
|
{
|
|
SANE_Status status;
|
|
DMC_Camera *c = ValidateHandle(handle);
|
|
size_t size;
|
|
SANE_Int i;
|
|
|
|
if (!c) return SANE_STATUS_INVAL;
|
|
|
|
if (c->fd < 0) return SANE_STATUS_INVAL;
|
|
|
|
if (c->bytes_to_read == 0) {
|
|
if (c->readBuffer) {
|
|
free(c->readBuffer);
|
|
c->readBuffer = NULL;
|
|
c->readPtr = NULL;
|
|
}
|
|
DMCCancel(c);
|
|
return SANE_STATUS_EOF;
|
|
}
|
|
|
|
if (max_len == 0) {
|
|
return SANE_STATUS_GOOD;
|
|
}
|
|
|
|
if (c->imageMode == IMAGE_SUPER_RES) {
|
|
/* We have to read *two* complete rows... */
|
|
max_len = (max_len / (2*c->params.bytes_per_line)) *
|
|
(2*c->params.bytes_per_line);
|
|
/* If user is trying to read less than two complete lines, fail */
|
|
if (max_len == 0) return SANE_STATUS_INVAL;
|
|
if ((unsigned int) max_len > c->bytes_to_read) max_len = c->bytes_to_read;
|
|
for (i=0; i<max_len; i += 2*c->params.bytes_per_line) {
|
|
c->bytes_to_read -= 2*c->params.bytes_per_line;
|
|
status = DMCReadTwoSuperResolutionLines(c, buf+i,
|
|
!c->bytes_to_read);
|
|
if (status != SANE_STATUS_GOOD) return status;
|
|
}
|
|
*len = max_len;
|
|
return SANE_STATUS_GOOD;
|
|
}
|
|
|
|
if (c->imageMode == IMAGE_MFI || c->imageMode == IMAGE_RAW) {
|
|
/* We have to read complete rows... */
|
|
max_len = (max_len / c->params.bytes_per_line) * c->params.bytes_per_line;
|
|
|
|
/* If user is trying to read less than one complete row, fail */
|
|
if (max_len == 0) return SANE_STATUS_INVAL;
|
|
if ((unsigned int) max_len > c->bytes_to_read) max_len = c->bytes_to_read;
|
|
c->bytes_to_read -= (unsigned int) max_len;
|
|
status = DMCRead(c->fd, 0x00, c->imageMode, buf, max_len, &size);
|
|
*len = size;
|
|
return status;
|
|
}
|
|
|
|
if ((unsigned int) max_len > c->bytes_to_read) max_len = c->bytes_to_read;
|
|
if (c->readPtr) {
|
|
*len = max_len;
|
|
memcpy(buf, c->readPtr, max_len);
|
|
c->readPtr += max_len;
|
|
c->bytes_to_read -= max_len;
|
|
return SANE_STATUS_GOOD;
|
|
}
|
|
|
|
/* Fill the read buffer completely */
|
|
c->readBuffer = malloc(c->bytes_to_read);
|
|
if (!c->readBuffer) return SANE_STATUS_NO_MEM;
|
|
c->readPtr = c->readBuffer;
|
|
status = DMCRead(c->fd, 0x00, c->imageMode, (SANE_Byte *) c->readBuffer,
|
|
c->bytes_to_read, &size);
|
|
*len = size;
|
|
if (status != SANE_STATUS_GOOD) return status;
|
|
if ((unsigned int) *len != c->bytes_to_read) return SANE_STATUS_IO_ERROR;
|
|
|
|
/* Now copy */
|
|
*len = max_len;
|
|
memcpy(buf, c->readPtr, max_len);
|
|
c->readPtr += max_len;
|
|
c->bytes_to_read -= max_len;
|
|
return SANE_STATUS_GOOD;
|
|
}
|
|
|
|
/**********************************************************************
|
|
//%FUNCTION: sane_cancel
|
|
//%ARGUMENTS:
|
|
// handle -- handle of device
|
|
//%RETURNS:
|
|
// Nothing
|
|
//%DESCRIPTION:
|
|
// A quick cancellation of the scane
|
|
// *********************************************************************/
|
|
void
|
|
sane_cancel (SANE_Handle handle)
|
|
{
|
|
DMC_Camera *c = ValidateHandle(handle);
|
|
if (!c) return;
|
|
|
|
DMCCancel(c);
|
|
}
|
|
|
|
SANE_Status
|
|
sane_set_io_mode (SANE_Handle handle, SANE_Bool non_blocking)
|
|
{
|
|
handle = handle;
|
|
non_blocking = non_blocking;
|
|
|
|
return SANE_STATUS_UNSUPPORTED;
|
|
}
|
|
|
|
SANE_Status
|
|
sane_get_select_fd (SANE_Handle handle, SANE_Int *fd)
|
|
{
|
|
handle = handle;
|
|
fd = fd;
|
|
|
|
return SANE_STATUS_UNSUPPORTED;
|
|
}
|