diff options
author | Jörg Frings-Fürst <debian@jff-webhosting.net> | 2014-10-06 14:00:40 +0200 |
---|---|---|
committer | Jörg Frings-Fürst <debian@jff-webhosting.net> | 2014-10-06 14:00:40 +0200 |
commit | 6e9c41a892ed0e0da326e0278b3221ce3f5713b8 (patch) | |
tree | 2e301d871bbeeb44aa57ff9cc070fcf3be484487 /backend/hpsj5s.c |
Initial import of sane-backends version 1.0.24-1.2
Diffstat (limited to 'backend/hpsj5s.c')
-rw-r--r-- | backend/hpsj5s.c | 1568 |
1 files changed, 1568 insertions, 0 deletions
diff --git a/backend/hpsj5s.c b/backend/hpsj5s.c new file mode 100644 index 0000000..75f3526 --- /dev/null +++ b/backend/hpsj5s.c @@ -0,0 +1,1568 @@ +/* sane - Scanner Access Now Easy. + Copyright (C) 2002 Max Vorobiev <pcwizard@telecoms.sins.ru> + 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. */ + +#define BUILD 3 + +#define BACKEND_NAME hpsj5s +#define HPSJ5S_CONFIG_FILE "hpsj5s.conf" + +#include "../include/sane/config.h" +#include "../include/sane/sane.h" +#include "../include/sane/sanei.h" +#include "../include/sane/saneopts.h" + +#include "../include/sane/sanei_config.h" +#include "../include/sane/sanei_backend.h" + +#include "hpsj5s.h" + +#include <string.h> +#include <stdlib.h> +#include <stdio.h> +#include <unistd.h> + + +#define LINES_TO_FEED 480 /*Default feed length */ + +static int scanner_d = -1; /*This is handler to the only-supported. Will be fixed. */ +static char scanner_path[PATH_MAX] = ""; /*String for device-file */ +static SANE_Byte bLastCalibration; /*Here we store calibration result */ +static SANE_Byte bCalibration; /*Here we store new calibration value */ +static SANE_Byte bHardwareState; /*Here we store copy of hardware flags register */ + +/*Here we store Parameters:*/ +static SANE_Word wWidth = 2570; /*Scan area width */ +static SANE_Word wResolution = 300; /*Resolution in DPI */ +static SANE_Frame wCurrentFormat = SANE_FRAME_GRAY; /*Type of colors in image */ +static SANE_Int wCurrentDepth = 8; /*Bits per pixel in image */ + +/*Here we count lines of every new image...*/ +static SANE_Word wVerticalResolution; + +/*Limits for resolution control*/ +static const SANE_Range ImageWidthRange = { + 0, /*minimal */ + 2570, /*maximum */ + 2 /*quant */ +}; + +static const SANE_Word ImageResolutionsList[] = { + 6, /*Number of resolutions */ + 75, + 100, + 150, + 200, + 250, + 300 +}; + +static SANE_Option_Descriptor sod[] = { + { /*Number of options */ + SANE_NAME_NUM_OPTIONS, + SANE_TITLE_NUM_OPTIONS, + SANE_DESC_NUM_OPTIONS, + SANE_TYPE_INT, + SANE_UNIT_NONE, + sizeof (SANE_Word), + SANE_CAP_SOFT_DETECT, + SANE_CONSTRAINT_NONE, + {NULL} /*No constraints required */ + } + , + { /*Width of scaned area */ + "width", + "Width", + "Width of area to scan", + SANE_TYPE_INT, + SANE_UNIT_PIXEL, + sizeof (SANE_Word), + SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT, + SANE_CONSTRAINT_RANGE, + {NULL} /*Range constrain setted in sane_init */ + } + , + { /*Resolution for scan */ + "resolution", + "Resolution", + "Image resolution", + SANE_TYPE_INT, + SANE_UNIT_DPI, + sizeof (SANE_Word), + SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT, + SANE_CONSTRAINT_WORD_LIST, + {NULL} /*Word list constrain setted in sane_init */ + } +}; + +static SANE_Parameters parms; + +/*Recalculate Lenght in dependace of resolution*/ +static SANE_Word +LengthForRes (SANE_Word Resolution, SANE_Word Length) +{ + switch (Resolution) + { + case 75: + return Length / 4; + case 100: + return Length / 3; + case 150: + return Length / 2; + case 200: + return Length * 2 / 3; + case 250: + return Length * 5 / 6; + case 300: + default: + return Length; + } +} + +static struct parport_list pl; /*List of detected parallel ports. */ + +SANE_Status +sane_init (SANE_Int * version_code, SANE_Auth_Callback authorize) +{ + char line[PATH_MAX]; /*Line from config file */ + int len; /*Length of string from config file */ + FILE *config_file; /*Handle to config file of this backend */ + + DBG_INIT (); + DBG (1, ">>sane_init"); + DBG (2, "sane_init: version_code %s 0, authorize %s 0\n", + version_code == 0 ? "=" : "!=", authorize == 0 ? "=" : "!="); + DBG (1, "sane_init: SANE hpsj5s backend version %d.%d.%d\n", + SANE_CURRENT_MAJOR, V_MINOR, BUILD); + + /*Inform about supported version */ + if (version_code) + *version_code = SANE_VERSION_CODE (SANE_CURRENT_MAJOR, V_MINOR, BUILD); + + /*Open configuration file for this backend */ + config_file = sanei_config_open (HPSJ5S_CONFIG_FILE); + + if (!config_file) /*Failed to open config file */ + { + DBG (1, "sane_init: no config file found."); + return SANE_STATUS_GOOD; + } + + /*Read line by line */ + while (sanei_config_read (line, PATH_MAX, config_file)) + { + if ((line[0] == '#') || (line[0] == '\0')) /*comment line or empty line */ + continue; + len = strlen (line); /*sanei_config_read guaranty, it's not more then PATH_MAX-1 */ + strcpy (scanner_path, line); /*so, we choose last in file (uncommented) */ + } + + fclose (config_file); /*We don't need config file any more */ + + /*sanei_config_attach_matching_devices(devname, attach_one); To do latter */ + + scanner_d = -1; /*scanner device not opened yet. */ + DBG (1, "<<sane_init"); + + /*Init params structure with defaults values: */ + wCurrentFormat = SANE_FRAME_GRAY; + wCurrentDepth = 8; + wWidth = 2570; + wResolution = 300; + + /*Setup some option descriptors */ + sod[1].constraint.range = &ImageWidthRange; /*Width option */ + sod[2].constraint.word_list = &ImageResolutionsList[0]; /*Resolution option */ + + /*Search for ports in system: */ + ieee1284_find_ports (&pl, 0); + + return SANE_STATUS_GOOD; +} + +void +sane_exit (void) +{ + if (scanner_d != -1) + { + CloseScanner (scanner_d); + scanner_d = -1; + } + + /*Free alocated ports information: */ + ieee1284_free_ports (&pl); + + DBG (2, "sane_exit\n"); + return; +} + +/* Device select/open/close */ +static const SANE_Device dev[] = { + { + "hpsj5s", + "Hewlett-Packard", + "ScanJet 5S", + "sheetfed scanner"} +}; + +SANE_Status +sane_get_devices (const SANE_Device *** device_list, SANE_Bool local_only) +{ + /*One device is supported and currently present */ + static const SANE_Device *devlist[] = { + dev + 0, 0 + }; + + /*No scanners presents */ + static const SANE_Device *void_devlist[] = { 0 }; + + DBG (2, "sane_get_devices: local_only = %d\n", local_only); + + if (scanner_d != -1) /*Device is opened, so it's present. */ + { + *device_list = devlist; + return SANE_STATUS_GOOD; + }; + + /*Device was not opened. */ + scanner_d = OpenScanner (scanner_path); + + if (scanner_d == -1) /*No devices present */ + { + DBG (1, "failed to open scanner.\n"); + *device_list = void_devlist; + return SANE_STATUS_GOOD; + } + DBG (1, "port opened.\n"); + + /*Check device. */ + DBG (1, "sane_get_devices: check scanner started."); + if (DetectScanner () == 0) + { /*Device malfunction! */ + DBG (1, "sane_get_devices: Device malfunction."); + *device_list = void_devlist; + return SANE_STATUS_GOOD; + } + else + { + DBG (1, "sane_get_devices: Device works OK."); + *device_list = devlist; + } + + /*We do not need it any more */ + CloseScanner (scanner_d); + scanner_d = -1; + + return SANE_STATUS_GOOD; +} + +SANE_Status +sane_open (SANE_String_Const devicename, SANE_Handle * handle) +{ + int i; + + if (!devicename) + { + DBG (1, "sane_open: devicename is NULL!"); + return SANE_STATUS_INVAL; + } + + DBG (2, "sane_open: devicename = \"%s\"\n", devicename); + + if (!devicename[0]) + i = 0; + else + for (i = 0; i < NELEMS (dev); ++i) /*Search for device in list */ + if (strcmp (devicename, dev[i].name) == 0) + break; + + if (i >= NELEMS (dev)) /*No such device */ + return SANE_STATUS_INVAL; + + if (scanner_d != -1) /*scanner opened already! */ + return SANE_STATUS_DEVICE_BUSY; + + DBG (1, "sane_open: scanner device path name is \'%s\'\n", scanner_path); + + scanner_d = OpenScanner (scanner_path); + if (scanner_d == -1) + return SANE_STATUS_DEVICE_BUSY; /*This should be done more carefully */ + + /*Check device. */ + DBG (1, "sane_open: check scanner started."); + if (DetectScanner () == 0) + { /*Device malfunction! */ + DBG (1, "sane_open: Device malfunction."); + CloseScanner (scanner_d); + scanner_d = -1; + return SANE_STATUS_IO_ERROR; + } + DBG (1, "sane_open: Device found.All are green."); + *handle = (SANE_Handle) (unsigned long)scanner_d; + + return SANE_STATUS_GOOD; +} + +void +sane_close (SANE_Handle handle) +{ + DBG (2, "sane_close\n"); + /*We support only single device - so ignore handle (FIX IT LATER) */ + if ((handle != (SANE_Handle) (unsigned long)scanner_d) || (scanner_d == -1)) + return; /* wrong device */ + StandByScanner (); + CloseScanner (scanner_d); + scanner_d = -1; +} + +const SANE_Option_Descriptor * +sane_get_option_descriptor (SANE_Handle handle, SANE_Int option) +{ + DBG (2, "sane_get_option_descriptor: option = %d\n", option); + if ((handle != (SANE_Handle) (unsigned long)scanner_d) || (scanner_d == -1)) + return NULL; /* wrong device */ + + if (option < 0 || option >= NELEMS (sod)) /*No real options supported */ + return NULL; + + return &sod[option]; /*Return demanded option */ +} + +SANE_Status +sane_control_option (SANE_Handle handle, SANE_Int option, + SANE_Action action, void *value, SANE_Int * info) +{ + if ((handle != (SANE_Handle) (unsigned long)scanner_d) || (scanner_d == -1)) + return SANE_STATUS_INVAL; /* wrong device */ + + if ((option >= NELEMS (sod)) || (option < 0)) /*Supported only this option */ + return SANE_STATUS_INVAL; + + switch (option) + { + case 0: /*Number of options */ + if (action != SANE_ACTION_GET_VALUE) /*It can be only read */ + return SANE_STATUS_INVAL; + + *((SANE_Int *) value) = NELEMS (sod); + return SANE_STATUS_GOOD; + case 1: /*Scan area width */ + switch (action) + { + case SANE_ACTION_GET_VALUE: + *((SANE_Word *) value) = wWidth; + return SANE_STATUS_GOOD; + case SANE_ACTION_SET_VALUE: /*info should be setted */ + wWidth = *((SANE_Word *) value); + if (info != NULL) + *info = SANE_INFO_RELOAD_PARAMS; + return SANE_STATUS_GOOD; + default: + return SANE_STATUS_INVAL; + } + case 2: /*Resolution */ + switch (action) + { + case SANE_ACTION_GET_VALUE: + *((SANE_Word *) value) = wResolution; + return SANE_STATUS_GOOD; + case SANE_ACTION_SET_VALUE: /*info should be setted */ + wResolution = *((SANE_Word *) value); + if (info != NULL) + *info = 0; + return SANE_STATUS_GOOD; + default: + return SANE_STATUS_INVAL; + } + default: + return SANE_STATUS_INVAL; + } + return SANE_STATUS_GOOD; /*For now we have no options to control */ +} + +SANE_Status +sane_get_parameters (SANE_Handle handle, SANE_Parameters * params) +{ + DBG (2, "sane_get_parameters\n"); + + if ((handle != (SANE_Handle) (unsigned long)scanner_d) || (scanner_d == -1)) + return SANE_STATUS_INVAL; /* wrong device */ + + /*Ignore handle parameter for now. FIX it latter. */ + /*These parameters are OK for gray scale mode. */ + parms.depth = /*wCurrentDepth */ 8; + parms.format = /*wCurrentFormat */ SANE_FRAME_GRAY; + parms.last_frame = SANE_TRUE; /*For grayscale... */ + parms.lines = -1; /*Unknown a priory */ + parms.pixels_per_line = LengthForRes (wResolution, wWidth); /*For grayscale... */ + parms.bytes_per_line = parms.pixels_per_line; /*For grayscale... */ + *params = parms; + return SANE_STATUS_GOOD; +} + +SANE_Status +sane_start (SANE_Handle handle) +{ + int i; + DBG (2, "sane_start\n"); + + if ((handle != (SANE_Handle) (unsigned long)scanner_d) || (scanner_d == -1)) + return SANE_STATUS_IO_ERROR; + + CallFunctionWithParameter (0x93, 2); + bLastCalibration = CallFunctionWithRetVal (0xA9); + if (bLastCalibration == 0) + bLastCalibration = -1; + + /*Turn on the lamp: */ + CallFunctionWithParameter (FUNCTION_SETUP_HARDWARE, FLAGS_HW_LAMP_ON); + bHardwareState = FLAGS_HW_LAMP_ON; + /*Get average white point */ + bCalibration = GetCalibration (); + + if (bLastCalibration - bCalibration > 16) + { /*Lamp is not warm enouth */ + DBG (1, "sane_start: warming lamp for 30 sec.\n"); + for (i = 0; i < 30; i++) + sleep (1); + } + + /*Check paper presents */ + if (CheckPaperPresent () == 0) + { + DBG (1, "sane_start: no paper detected."); + return SANE_STATUS_NO_DOCS; + } + CalibrateScanElements (); + TransferScanParameters (GrayScale, wResolution, wWidth); + /*Turn on indicator and prepare engine. */ + SwitchHardwareState (FLAGS_HW_INDICATOR_OFF | FLAGS_HW_MOTOR_READY, 1); + /*Feed paper */ + if (PaperFeed (LINES_TO_FEED) == 0) /*Feed only for fixel lenght. Change it */ + { + DBG (1, "sane_start: paper feed failed."); + SwitchHardwareState (FLAGS_HW_INDICATOR_OFF | FLAGS_HW_MOTOR_READY, 0); + return SANE_STATUS_JAMMED; + } + /*Set paper moving speed */ + TurnOnPaperPulling (GrayScale, wResolution); + + wVerticalResolution = 0; /*Reset counter */ + + return SANE_STATUS_GOOD; +} + +SANE_Status +sane_read (SANE_Handle handle, SANE_Byte * data, + SANE_Int max_length, SANE_Int * length) +{ + SANE_Byte bFuncResult, bTest; + int timeout; + + if (!length) + { + DBG (1, "sane_read: length == NULL\n"); + return SANE_STATUS_INVAL; + } + *length = 0; + if (!data) + { + DBG (1, "sane_read: data == NULL\n"); + return SANE_STATUS_INVAL; + } + + if ((handle != (SANE_Handle) (unsigned long)scanner_d) || (scanner_d == -1)) + { + DBG (1, "sane_read: unknown handle\n"); + return SANE_STATUS_INVAL; + } + + /*While end of paper sheet was not reached */ + /*Wait for scaned line ready */ + timeout = 0; + while (((bFuncResult = CallFunctionWithRetVal (0xB2)) & 0x20) == 0) + { + bTest = CallFunctionWithRetVal (0xB5); + usleep (1); + timeout++; + if ((timeout < 1000) && + (((bTest & 0x80) && ((bTest & 0x3F) <= 2)) || + (((bTest & 0x80) == 0) && ((bTest & 0x3F) >= 5)))) + continue; + + if (timeout >= 1000) + continue; /*do it again! */ + + /*Data ready state! */ + + if ((bFuncResult & 0x20) != 0) /*End of paper reached! */ + { + *length = 0; + return SANE_STATUS_EOF; + } + + /*Data ready */ + *length = LengthForRes (wResolution, wWidth); + if (*length >= max_length) + *length = max_length; + + CallFunctionWithParameter (0xCD, 0); + CallFunctionWithRetVal (0xC8); + WriteScannerRegister (REGISTER_FUNCTION_CODE, 0xC8); + WriteAddress (ADDRESS_RESULT); + /*Test if we need this line for current resolution + (scanner doesn't control vertical resolution in hardware) */ + wVerticalResolution -= wResolution; + if (wVerticalResolution > 0) + { + timeout = 0; + continue; + } + else + wVerticalResolution = 300; /*Reset counter */ + + ReadDataBlock (data, *length); + + /*switch indicator */ + bHardwareState ^= FLAGS_HW_INDICATOR_OFF; + CallFunctionWithParameter (FUNCTION_SETUP_HARDWARE, bHardwareState); + return SANE_STATUS_GOOD; + } + return SANE_STATUS_EOF; +} + +void +sane_cancel (SANE_Handle handle) +{ + DBG (2, "sane_cancel: handle = %p\n", handle); + /*Stop motor */ + TurnOffPaperPulling (); + + /*Indicator turn off */ + bHardwareState |= FLAGS_HW_INDICATOR_OFF; + CallFunctionWithParameter (FUNCTION_SETUP_HARDWARE, bHardwareState); + + /*Get out of paper */ + ReleasePaper (); + + /*Restore indicator */ + bHardwareState &= ~FLAGS_HW_INDICATOR_OFF; + CallFunctionWithParameter (FUNCTION_SETUP_HARDWARE, bHardwareState); + + bLastCalibration = CallFunctionWithRetVal (0xA9); + CallFunctionWithParameter (0xA9, bLastCalibration); + CallFunctionWithParameter (0x93, 4); + +} + +SANE_Status +sane_set_io_mode (SANE_Handle handle, SANE_Bool non_blocking) +{ + DBG (2, "sane_set_io_mode: handle = %p, non_blocking = %d\n", handle, + non_blocking); + return SANE_STATUS_UNSUPPORTED; +} + +SANE_Status +sane_get_select_fd (SANE_Handle handle, SANE_Int * fd) +{ + DBG (2, "sane_get_select_fd: handle = %p, fd %s 0\n", handle, + fd ? "!=" : "="); + return SANE_STATUS_UNSUPPORTED; +} + +/* + Middle-level API: +*/ + +/* + Detect if scanner present and works correctly. + Ret Val: 0 = detection failed, 1 = detection OK. +*/ +static int +DetectScanner (void) +{ + int Result1, Result2; + int Successful, Total; + + Result1 = OutputCheck (); + Result2 = InputCheck (); + + if (!(Result1 || Result2)) /*If all are 0 - it's error */ + { + return 0; + } + + WriteScannerRegister (0x7C, 0x80); + WriteScannerRegister (0x7F, 0x1); + WriteScannerRegister (0x72, 0x10); + WriteScannerRegister (0x72, 0x90); + WriteScannerRegister (0x7C, 0x24); + WriteScannerRegister (0x75, 0x0C); + WriteScannerRegister (0x78, 0x0); + WriteScannerRegister (0x79, 0x10); + WriteScannerRegister (0x71, 0x10); + WriteScannerRegister (0x71, 0x1); + WriteScannerRegister (0x72, 0x1); + + for (Successful = 0, Total = 0; Total < 5; Total++) + { + if (CallCheck ()) + Successful++; + if (Successful >= 3) + return 1; /*Correct and Stable */ + } + return 0; +} + +static void +StandByScanner () +{ + WriteScannerRegister (0x74, 0x80); + WriteScannerRegister (0x75, 0x0C); + WriteScannerRegister (0x77, 0x0); + WriteScannerRegister (0x78, 0x0); + WriteScannerRegister (0x79, 0x0); + WriteScannerRegister (0x7A, 0x0); + WriteScannerRegister (0x7B, 0x0); + WriteScannerRegister (0x7C, 0x4); + WriteScannerRegister (0x70, 0x0); + WriteScannerRegister (0x72, 0x90); + WriteScannerRegister (0x70, 0x0); +} + +static void +SwitchHardwareState (SANE_Byte mask, SANE_Byte invert_mask) +{ + if (!invert_mask) + { + bHardwareState &= ~mask; + } + else + bHardwareState |= mask; + + CallFunctionWithParameter (FUNCTION_SETUP_HARDWARE, bHardwareState); +} + +/*return value: 0 - no paper, 1 - paper loaded.*/ +static int +CheckPaperPresent () +{ + if ((CallFunctionWithRetVal (0xB2) & 0x10) == 0) + return 1; /*Ok - paper present. */ + return 0; /*No paper present */ +} + +static int +ReleasePaper () +{ + int i; + + if ((CallFunctionWithRetVal (0xB2) & 0x20) == 0) + { /*End of paper was not reached */ + CallFunctionWithParameter (0xA7, 0xF); + CallFunctionWithParameter (0xA8, 0xFF); + CallFunctionWithParameter (0xC2, 0); + + for (i = 0; i < 90000; i++) + { + if (CallFunctionWithRetVal (0xB2) & 0x80) + break; + usleep (1); + } + if (i >= 90000) + return 0; /*Fail. */ + + for (i = 0; i < 90000; i++) + { + if ((CallFunctionWithRetVal (0xB2) & 0x20) == 0) + break; + else if ((CallFunctionWithRetVal (0xB2) & 0x80) == 0) + { + i = 90000; + break; + } + usleep (1); + } + + CallFunctionWithParameter (0xC5, 0); + + if (i >= 90000) + return 0; /*Fail. */ + + while (CallFunctionWithRetVal (0xB2) & 0x80); /*Wait bit dismiss */ + + CallFunctionWithParameter (0xA7, 1); + CallFunctionWithParameter (0xA8, 0x25); + CallFunctionWithParameter (0xC2, 0); + + for (i = 0; i < 90000; i++) + { + if (CallFunctionWithRetVal (0xB2) & 0x80) + break; + usleep (1); + } + if (i >= 90000) + return 0; /*Fail. */ + + for (i = 0; i < 90000; i++) + { + if ((CallFunctionWithRetVal (0xB2) & 0x80) == 0) + break; + usleep (1); + } + if (i >= 90000) + return 0; /*Fail. */ + } + + if (CallFunctionWithRetVal (0xB2) & 0x10) + { + CallFunctionWithParameter (0xA7, 1); + CallFunctionWithParameter (0xA8, 0x40); + } + else + { + CallFunctionWithParameter (0xA7, 0); + CallFunctionWithParameter (0xA8, 0xFA); + } + CallFunctionWithParameter (0xC2, 0); + + for (i = 0; i < 9000; i++) + { + if (CallFunctionWithRetVal (0xB2) & 0x80) + break; + usleep (1); + } + if (i >= 9000) + return 0; /*Fail. */ + + while (CallFunctionWithRetVal (0xB2) & 0x80) + usleep (1); + + return 1; +} + +static void +TransferScanParameters (enumColorDepth enColor, SANE_Word wResolution, + SANE_Word wPixelsLength) +{ + SANE_Word wRightBourder = (2570 + wPixelsLength) / 2 + 65; + SANE_Word wLeftBourder = (2570 - wPixelsLength) / 2 + 65; + + switch (enColor) + { + case Drawing: + CallFunctionWithParameter (0x90, 2); /*Not supported correctle. FIX ME!!! */ + break; + case Halftone: + CallFunctionWithParameter (0x90, 0xE3); /*Not supported correctly. FIX ME!!! */ + CallFunctionWithParameter (0x92, 3); + break; + case GrayScale: + case TrueColor: + CallFunctionWithParameter (0x90, 0); /*Not suppoted correctly. FIX ME!!! */ + break; + }; + CallFunctionWithParameter (0xA1, 2); + CallFunctionWithParameter (0xA2, 1); + CallFunctionWithParameter (0xA3, 0x98); + /*Resolution: */ + CallFunctionWithParameter (0x9A, (SANE_Byte) (wResolution >> 8)); /*High byte */ + CallFunctionWithParameter (0x9B, (SANE_Byte) wResolution); /*Low byte */ + + LoadingPaletteToScanner (); + + CallFunctionWithParameter (0xA4, 31); /*Some sort of constant parameter */ + /*Left bourder */ + CallFunctionWithParameter (0xA5, wLeftBourder / 256); + CallFunctionWithParameter (0xA6, wLeftBourder % 256); + /*Right bourder */ + CallFunctionWithParameter (0xAA, wRightBourder / 256); + CallFunctionWithParameter (0xAB, wRightBourder % 256); + + CallFunctionWithParameter (0xD0, 0); + CallFunctionWithParameter (0xD1, 0); + CallFunctionWithParameter (0xD2, 0); + CallFunctionWithParameter (0xD3, 0); + CallFunctionWithParameter (0xD4, 0); + CallFunctionWithParameter (0xD5, 0); + + CallFunctionWithParameter (0x9D, 5); +} + +static void +TurnOnPaperPulling (enumColorDepth enColor, SANE_Word wResolution) +{ + switch (enColor) + { + case Drawing: + case Halftone: + CallFunctionWithParameter (0x91, 0xF7); + return; + case GrayScale: + switch (wResolution) + { + case 50: + case 75: + case 100: + CallFunctionWithParameter (0x91, 0xB7); + return; + case 150: + case 200: + CallFunctionWithParameter (0x91, 0x77); + return; + case 250: + case 300: + CallFunctionWithParameter (0x91, 0x37); + return; + default: + return; + } + case TrueColor: + switch (wResolution) + { + case 75: + case 100: + CallFunctionWithParameter (0x91, 0xA3); + return; + case 150: + case 200: + CallFunctionWithParameter (0x91, 0x53); + return; + case 250: + case 300: + CallFunctionWithParameter (0x91, 0x3); + return; + default: + return; + } + default: + return; + } +} + +static void +TurnOffPaperPulling () +{ + CallFunctionWithParameter (0x91, 0); +} + +/* + Returns avarage value of scaned row. + While paper not loaded this is base "white point". +*/ +static SANE_Byte +GetCalibration () +{ + int i; + int Result; + SANE_Byte Buffer[2600]; + SANE_Byte bTest; + + CallFunctionWithParameter (0xA1, 2); + CallFunctionWithParameter (0xA2, 1); + CallFunctionWithParameter (0xA3, 0x98); + + /*Resolution to 300 DPI */ + CallFunctionWithParameter (0x9A, 1); + CallFunctionWithParameter (0x9B, 0x2C); + + CallFunctionWithParameter (0x92, 0); + CallFunctionWithParameter (0xC6, 0); + CallFunctionWithParameter (0x92, 0x80); + + for (i = 1; i < 256; i++) + CallFunctionWithParameter (0xC6, i); + + for (i = 0; i < 256; i++) + CallFunctionWithParameter (0xC6, i); + + for (i = 0; i < 256; i++) + CallFunctionWithParameter (0xC6, i); + + CallFunctionWithParameter (0xA4, 31); /*Some sort of constant */ + + /*Left bourder */ + CallFunctionWithParameter (0xA5, 0); + CallFunctionWithParameter (0xA6, 0x41); + + /*Right bourder */ + CallFunctionWithParameter (0xAA, 0xA); + CallFunctionWithParameter (0xAB, 0x39); + + CallFunctionWithParameter (0xD0, 0); + CallFunctionWithParameter (0xD1, 0); + CallFunctionWithParameter (0xD2, 0); + CallFunctionWithParameter (0xD3, 0); + CallFunctionWithParameter (0xD4, 0); + CallFunctionWithParameter (0xD5, 0); + + CallFunctionWithParameter (0x9C, 0x1B); + CallFunctionWithParameter (0x9D, 5); + + CallFunctionWithParameter (0x92, 0x10); + CallFunctionWithParameter (0xC6, 0xFF); + CallFunctionWithParameter (0x92, 0x90); + + for (i = 0; i < 2999; i++) + CallFunctionWithParameter (0xC6, 0xFF); + + CallFunctionWithParameter (0x92, 0x50); + CallFunctionWithParameter (0xC6, 0); + CallFunctionWithParameter (0x92, 0xD0); + + for (i = 0; i < 2999; i++) + CallFunctionWithParameter (0xC6, 0); + + CallFunctionWithParameter (0x98, 0xFF); /*Up limit */ + CallFunctionWithParameter (0x95, 0); /*Low limit */ + + CallFunctionWithParameter (0x90, 0); /*Gray scale... */ + + CallFunctionWithParameter (0x91, 0x3B); /*Turn motor on. */ + + for (i = 0; i < 5; i++) + { + do + { /*WARNING!!! Deadlock possible! */ + bTest = CallFunctionWithRetVal (0xB5); + } + while ((((bTest & 0x80) == 1) && ((bTest & 0x3F) <= 2)) || + (((bTest & 0x80) == 0) && ((bTest & 0x3F) >= 5))); + + CallFunctionWithParameter (0xCD, 0); + /*Skip this line for ECP: */ + CallFunctionWithRetVal (0xC8); + + WriteScannerRegister (REGISTER_FUNCTION_CODE, 0xC8); + WriteAddress (0x20); + ReadDataBlock (Buffer, 2552); + }; + CallFunctionWithParameter (0x91, 0); /*Turn off motor. */ + usleep (10); + for (Result = 0, i = 0; i < 2552; i++) + Result += Buffer[i]; + return Result / 2552; +} + +static int +PaperFeed (SANE_Word wLinesToFeed) +{ + int i; + + CallFunctionWithParameter (0xA7, 0xF); + CallFunctionWithParameter (0xA8, 0xFF); + CallFunctionWithParameter (0xC2, 0); + + for (i = 0; i < 9000; i++) + { + if (CallFunctionWithRetVal (0xB2) & 0x80) + break; + usleep (1); + } + if (i >= 9000) + return 0; /*Fail. */ + + for (i = 0; i < 9000; i += 5) + { + if ((CallFunctionWithRetVal (0xB2) & 0x20) == 0) + break; + else if ((CallFunctionWithRetVal (0xB2) & 0x80) == 0) + { + i = 9000; + break; + } + usleep (5); + } + + CallFunctionWithParameter (0xC5, 0); + + if (i >= 9000) + return 0; /*Fail. */ + + /*Potential deadlock */ + while (CallFunctionWithRetVal (0xB2) & 0x80); /*Wait bit dismiss */ + + CallFunctionWithParameter (0xA7, wLinesToFeed / 256); + CallFunctionWithParameter (0xA8, wLinesToFeed % 256); + CallFunctionWithParameter (0xC2, 0); + + for (i = 0; i < 9000; i++) + { + if (CallFunctionWithRetVal (0xB2) & 0x80) + break; + usleep (1); + } + if (i >= 9000) + return 0; /*Fail. */ + + for (i = 0; i < 9000; i++) + { + if ((CallFunctionWithRetVal (0xB2) & 0x80) == 0) + break; + usleep (1); + } + if (i >= 9000) + return 0; /*Fail. */ + + return 1; +} + +/*For now we do no calibrate elements - just set maximum limits. FIX ME?*/ +static void +CalibrateScanElements () +{ + /*Those arrays will be used in future for correct calibration. */ + /*Then we need to transfer UP brightness border, we use these registers */ + SANE_Byte arUpTransferBorders[] = { 0x10, 0x20, 0x30 }; + /*Then we need to transfer LOW brightness border, we use these registers */ + SANE_Byte arLowTransferBorders[] = { 0x50, 0x60, 0x70 }; + /*Then we need to save UP brightness border, we use these registers */ + SANE_Byte arUpSaveBorders[] = { 0x98, 0x97, 0x99 }; + /*Then we need to save LOW brightness border, we use these registers */ + SANE_Byte arLowSaveBorders[] = { 0x95, 0x94, 0x96 }; + /*Speeds, used for calibration */ + SANE_Byte arSpeeds[] = { 0x3B, 0x37, 0x3F }; + int j, Average, Temp, Index, /* Line, */ timeout,Calibration; + SANE_Byte bTest /*, Min, Max, Result */ ; + /*For current color component: (values from arrays). Next two lines - starting and terminating. */ + + SANE_Byte CurrentUpTransferBorder; + SANE_Byte CurrentLowTransferBorder; + SANE_Byte CurrentUpSaveBorder; + SANE_Byte CurrentLowSaveBorder; + SANE_Byte CurrentSpeed1, CurrentSpeed2; + SANE_Byte CorrectionValue; + SANE_Byte FilteredBuffer[2570]; + + CallFunctionWithParameter (0xA1, 2); + CallFunctionWithParameter (0xA2, 0); + CallFunctionWithParameter (0xA3, 0x98); + + /*DPI = 300 */ + CallFunctionWithParameter (0x9A, 1); /*High byte */ + CallFunctionWithParameter (0x9B, 0x2C); /*Low byte */ + + /*Paletter settings. */ + CallFunctionWithParameter (0x92, 0); + CallFunctionWithParameter (0xC6, 0); + CallFunctionWithParameter (0x92, 0x80); + + /*First color component */ + for (j = 1; j < 256; j++) + CallFunctionWithParameter (0xC6, j); + + /*Second color component */ + for (j = 0; j < 256; j++) + CallFunctionWithParameter (0xC6, j); + + /*Third color component */ + for (j = 0; j < 256; j++) + CallFunctionWithParameter (0xC6, j); + + CallFunctionWithParameter (0xA4, 31); + + /*Left border */ + CallFunctionWithParameter (0xA5, 0); /*High byte */ + CallFunctionWithParameter (0xA6, 0x41); /*Low byte */ + + /*Right border */ + CallFunctionWithParameter (0xAA, 0xA); /*High byte */ + CallFunctionWithParameter (0xAB, 0x4B); /*Low byte */ + + /*Zero these registers... */ + CallFunctionWithParameter (0xD0, 0); + CallFunctionWithParameter (0xD1, 0); + CallFunctionWithParameter (0xD2, 0); + CallFunctionWithParameter (0xD3, 0); + CallFunctionWithParameter (0xD4, 0); + CallFunctionWithParameter (0xD5, 0); + + CallFunctionWithParameter (0x9C, 0x1B); + CallFunctionWithParameter (0x9D, 0x5); + + Average = 0; + for (Index = 0; Index < 3; Index++) /*For theree color components */ + { + /*Up border = 0xFF */ + CallFunctionWithParameter (0x92, arUpTransferBorders[Index]); + CallFunctionWithParameter (0xC6, 0xFF); + CallFunctionWithParameter (0x92, arUpTransferBorders[Index] | 0x80); + + for (j = 2999; j > 0; j--) + CallFunctionWithParameter (0xC6, 0xFF); + + /*Low border = 0x0 */ + CallFunctionWithParameter (0x92, arLowTransferBorders[Index]); + CallFunctionWithParameter (0xC6, 0x0); + CallFunctionWithParameter (0x92, arLowTransferBorders[Index] | 0x80); + + for (j = 2999; j > 0; j--) + CallFunctionWithParameter (0xC6, 0x0); + + /*Save borders */ + CallFunctionWithParameter (arUpSaveBorders[Index], 0xFF); + CallFunctionWithParameter (arLowSaveBorders[Index], 0x0); + CallFunctionWithParameter (0x90, 0); /*Gray Scale or True color sign :) */ + + CallFunctionWithParameter (0x91, arSpeeds[Index]); + + /*waiting for scaned line... */ + timeout = 0; + do + { + bTest = CallFunctionWithRetVal (0xB5); + timeout++; + usleep (1); + } + while ((timeout < 1000) && + ((((bTest & 0x80) == 1) && ((bTest & 0x3F) <= 2)) || + (((bTest & 0x80) == 0) && ((bTest & 0x3F) >= 5)))); + + /*Let's read it... */ + if(timeout < 1000) + { + CallFunctionWithParameter (0xCD, 0); + CallFunctionWithRetVal (0xC8); + WriteScannerRegister (0x70, 0xC8); + WriteAddress (0x20); + + ReadDataBlock (FilteredBuffer, 2570); + } + + CallFunctionWithParameter (0x91, 0); /*Stop engine. */ + + /*Note: if first read failed, junk would be calculated, but if previous + read was succeded, but last one failed, previous data'ld be used. + */ + for(Temp = 0, j = 0; j < 2570; j++) + Temp += FilteredBuffer[j]; + Temp /= 2570; + + if((Average == 0)||(Average > Temp)) + Average = Temp; + } + + for(Index = 0; Index < 3; Index++) /*Three color components*/ + { + CurrentUpTransferBorder = arUpTransferBorders[Index]; + CallFunctionWithParameter (0xC6, 0xFF); + CallFunctionWithParameter (0x92, CurrentUpTransferBorder|0x80); + for(j=2999; j>0; j--) + CallFunctionWithParameter (0xC6, 0xFF); + + CurrentLowTransferBorder = arLowTransferBorders[Index]; + CallFunctionWithParameter (0xC6, 0x0); + CallFunctionWithParameter (0x92, CurrentLowTransferBorder|0x80); + for(j=2999; j>0; j--) + CallFunctionWithParameter (0xC6, 0); + + CurrentUpSaveBorder = arUpSaveBorders[Index]; + CallFunctionWithParameter (CurrentUpSaveBorder, 0xFF); + + CurrentLowSaveBorder = arLowSaveBorders[Index]; + CallFunctionWithParameter (CurrentLowSaveBorder, 0x0); + CallFunctionWithParameter (0x90,0); + Calibration = 0x80; + CallFunctionWithParameter (CurrentUpSaveBorder, 0x80); + + CurrentSpeed1 = CurrentSpeed2 = arSpeeds[Index]; + + for(CorrectionValue = 0x40; CorrectionValue != 0;CorrectionValue >>= 2) + { + CallFunctionWithParameter (0x91, CurrentSpeed2); + usleep(10); + + /*waiting for scaned line... */ + for(j = 0; j < 5; j++) + { + timeout = 0; + do + { + bTest = CallFunctionWithRetVal (0xB5); + timeout++; + usleep (1); + } + while ((timeout < 1000) && + ((((bTest & 0x80) == 1) && ((bTest & 0x3F) <= 2)) || + (((bTest & 0x80) == 0) && ((bTest & 0x3F) >= 5)))); + + /*Let's read it... */ + if(timeout < 1000) + { + CallFunctionWithParameter (0xCD, 0); + CallFunctionWithRetVal (0xC8); + WriteScannerRegister (0x70, 0xC8); + WriteAddress (0x20); + + ReadDataBlock (FilteredBuffer, 2570); + } + }/*5 times we read. I don't understand what for, but so does HP's driver. + Perhaps, we can optimize it in future.*/ + WriteScannerRegister (0x91, 0); + usleep(10); + + for(Temp = 0,j = 0; j < 16;j++) + Temp += FilteredBuffer[509+j]; /*At this offset calcalates HP's driver.*/ + Temp /= 16; + + if(Average > Temp) + { + Calibration += CorrectionValue; + Calibration = 0xFF < Calibration ? 0xFF : Calibration; /*min*/ + } + else + Calibration -= CorrectionValue; + + WriteScannerRegister (CurrentUpSaveBorder, Calibration); + }/*By CorrectionValue we tune UpSaveBorder*/ + + WriteScannerRegister (0x90, 8); + WriteScannerRegister (0x91, CurrentSpeed1); + usleep(10); + }/*By color components*/ + + return; +} + +/* + Internal use functions: +*/ + +/*Returns 0 in case of fail and 1 in success.*/ +static int +OutputCheck () +{ + int i; + + WriteScannerRegister (0x7F, 0x1); + WriteAddress (0x7E); + for (i = 0; i < 256; i++) + WriteData ((SANE_Byte) i); + + WriteAddress (0x3F); + if (ReadDataByte () & 0x80) + return 0; + + return 1; +} + +static int +InputCheck () +{ + int i; + SANE_Byte Buffer[256]; + + WriteAddress (0x3E); + for (i = 0; i < 256; i++) + { + Buffer[i] = ReadDataByte (); + } + + for (i = 0; i < 256; i++) + { + if (Buffer[i] != i) + return 0; + } + + return 1; +} + +static int +CallCheck () +{ + int i; + SANE_Byte Buffer[256]; + + CallFunctionWithParameter (0x92, 0x10); + CallFunctionWithParameter (0xC6, 0x0); + CallFunctionWithParameter (0x92, 0x90); + WriteScannerRegister (REGISTER_FUNCTION_CODE, 0xC6); + + WriteAddress (0x60); + + for (i = 1; i < 256; i++) + WriteData ((SANE_Byte) i); + + CallFunctionWithParameter (0x92, 0x10); + CallFunctionWithRetVal (0xC6); + CallFunctionWithParameter (0x92, 0x90); + WriteScannerRegister (REGISTER_FUNCTION_CODE, 0xC6); + + WriteAddress (ADDRESS_RESULT); + + ReadDataBlock (Buffer, 256); + + for (i = 0; i < 255; i++) + { + if (Buffer[i + 1] != (SANE_Byte) i) + return 0; + } + return 1; +} + +static void +LoadingPaletteToScanner () +{ + /*For now we have statical gamma. */ + SANE_Byte Gamma[256]; + int i; + for (i = 0; i < 256; i++) + Gamma[i] = i; + + CallFunctionWithParameter (0x92, 0); + CallFunctionWithParameter (0xC6, Gamma[0]); + CallFunctionWithParameter (0x92, 0x80); + for (i = 1; i < 256; i++) + CallFunctionWithParameter (0xC6, Gamma[i]); + + for (i = 0; i < 256; i++) + CallFunctionWithParameter (0xC6, Gamma[i]); + + for (i = 0; i < 256; i++) + CallFunctionWithParameter (0xC6, Gamma[i]); +} + +/* + Low level warappers: +*/ +static void +WriteAddress (SANE_Byte Address) +{ + ieee1284_data_dir (pl.portv[scanner_d], 0); /*Forward mode */ + ieee1284_frob_control (pl.portv[scanner_d], C1284_NINIT, C1284_NINIT); + ieee1284_epp_write_addr (pl.portv[scanner_d], 0, (char *) &Address, 1); +} + +static void +WriteData (SANE_Byte Data) +{ + ieee1284_data_dir (pl.portv[scanner_d], 0); /*Forward mode */ + ieee1284_frob_control (pl.portv[scanner_d], C1284_NINIT, C1284_NINIT); + ieee1284_epp_write_data (pl.portv[scanner_d], 0, (char *) &Data, 1); +} + +static void +WriteScannerRegister (SANE_Byte Address, SANE_Byte Data) +{ + WriteAddress (Address); + WriteData (Data); +} + +static void +CallFunctionWithParameter (SANE_Byte Function, SANE_Byte Parameter) +{ + WriteScannerRegister (REGISTER_FUNCTION_CODE, Function); + WriteScannerRegister (REGISTER_FUNCTION_PARAMETER, Parameter); +} + +static SANE_Byte +CallFunctionWithRetVal (SANE_Byte Function) +{ + WriteScannerRegister (REGISTER_FUNCTION_CODE, Function); + WriteAddress (ADDRESS_RESULT); + return ReadDataByte (); +} + +static SANE_Byte +ReadDataByte () +{ + SANE_Byte Result; + + ieee1284_data_dir (pl.portv[scanner_d], 1); /*Reverse mode */ + ieee1284_frob_control (pl.portv[scanner_d], C1284_NINIT, C1284_NINIT); + ieee1284_epp_read_data (pl.portv[scanner_d], 0, (char *) &Result, 1); + return Result; +} + +static void +ReadDataBlock (SANE_Byte * Buffer, int length) +{ + + ieee1284_data_dir (pl.portv[scanner_d], 1); /*Reverse mode */ + ieee1284_frob_control (pl.portv[scanner_d], C1284_NINIT, C1284_NINIT); + ieee1284_epp_read_data (pl.portv[scanner_d], 0, (char *) Buffer, length); +} + +/* Send a daisy-chain-style CPP command packet. */ +int +cpp_daisy (struct parport *port, int cmd) +{ + unsigned char s; + + ieee1284_data_dir (port, 0); /*forward direction */ + ieee1284_write_control (port, C1284_NINIT); + ieee1284_write_data (port, 0xaa); + usleep (2); + ieee1284_write_data (port, 0x55); + usleep (2); + ieee1284_write_data (port, 0x00); + usleep (2); + ieee1284_write_data (port, 0xff); + usleep (2); + s = ieee1284_read_status (port) ^ S1284_INVERTED; /*Converted for PC-style */ + + s &= (S1284_BUSY | S1284_PERROR | S1284_SELECT | S1284_NFAULT); + + if (s != (S1284_BUSY | S1284_PERROR | S1284_SELECT | S1284_NFAULT)) + { + DBG (1, "%s: cpp_daisy: aa5500ff(%02x)\n", port->name, s); + return -1; + } + + ieee1284_write_data (port, 0x87); + usleep (2); + s = ieee1284_read_status (port) ^ S1284_INVERTED; /*Convert to PC-style */ + + s &= (S1284_BUSY | S1284_PERROR | S1284_SELECT | S1284_NFAULT); + + if (s != (S1284_SELECT | S1284_NFAULT)) + { + DBG (1, "%s: cpp_daisy: aa5500ff87(%02x)\n", port->name, s); + return -1; + } + + ieee1284_write_data (port, 0x78); + usleep (2); + ieee1284_write_control (port, C1284_NINIT); + ieee1284_write_data (port, cmd); + usleep (2); + ieee1284_frob_control (port, C1284_NSTROBE, C1284_NSTROBE); + usleep (1); + ieee1284_frob_control (port, C1284_NSTROBE, 0); + usleep (1); + s = ieee1284_read_status (port); + ieee1284_write_data (port, 0xff); + usleep (2); + + return s; +} + +/*Daisy chain deselect operation.*/ +void +daisy_deselect_all (struct parport *port) +{ + cpp_daisy (port, 0x30); +} + +/*Daisy chain select operation*/ +int +daisy_select (struct parport *port, int daisy, int mode) +{ + switch (mode) + { + /*For these modes we should switch to EPP mode: */ + case M1284_EPP: + case M1284_EPPSL: + case M1284_EPPSWE: + return cpp_daisy (port, 0x20 + daisy) & S1284_NFAULT; + /*For these modes we should switch to ECP mode: */ + case M1284_ECP: + case M1284_ECPRLE: + case M1284_ECPSWE: + return cpp_daisy (port, 0xd0 + daisy) & S1284_NFAULT; + /*Nothing was told for BECP in Daisy chain specification. + May be it's wise to use ECP? */ + case M1284_BECP: + /*Others use compat mode */ + case M1284_NIBBLE: + case M1284_BYTE: + case M1284_COMPAT: + default: + return cpp_daisy (port, 0xe0 + daisy) & S1284_NFAULT; + } +} + +/*Daisy chain assign address operation.*/ +int +assign_addr (struct parport *port, int daisy) +{ + return cpp_daisy (port, daisy); +} + +static int +OpenScanner (const char *scanner_path) +{ + int handle; + int caps; + + /*Scaner name was specified in config file?*/ + if (strlen(scanner_path) == 0) + return -1; + + for (handle = 0; handle < pl.portc; handle++) + { + if (strcmp (scanner_path, pl.portv[handle]->name) == 0) + break; + } + if (handle == pl.portc) /*No match found */ + return -1; + + /*Open port */ + if (ieee1284_open (pl.portv[handle], 0, &caps) != E1284_OK) + return -1; + + /*Claim port */ + if (ieee1284_claim (pl.portv[handle]) != E1284_OK) + return -1; + + /*Total chain reset. */ + daisy_deselect_all (pl.portv[handle]); + + /*Assign addresses. */ + assign_addr (pl.portv[handle], 0); /*Assume we have device first in chain. */ + + /*Select required device. For now - first in chain. */ + daisy_select (pl.portv[handle], 0, M1284_EPP); + + return handle; +} + +static void +CloseScanner (int handle) +{ + if (handle == -1) + return; + + daisy_deselect_all (pl.portv[handle]); + + ieee1284_release (pl.portv[handle]); + + ieee1284_close (pl.portv[handle]); +} |