diff --git a/ChangeLog b/ChangeLog index 444c8a5ac..124974629 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,18 @@ +2008-11-29 m. allan noah + * backend/canon_dr.[ch]: backend v7 + - jpeg support (size rounding and header overwrite) + - call object_position(load) between pages even if buffering is on + - use request sense info bytes on short scsi reads + - byte swap color BGR to RGB + - round image width down, not up + - round image height down to even # of lines + - always transfer even # of lines per block + - scsi and jpeg don't require reading extra lines to reach EOF + - rename buffer option to buffermode to avoid conflict with scanimage + - send ssm_do and ssm_df during sane_start + - improve sense_handler output + * doc/descriptions/canon_dr.desc: backend v7 + 2008-11-29 m. allan noah * backend/canon_dr.[ch], backend/canon_dr-cmd.c: backend v6 - fix adf simplex diff --git a/backend/canon_dr.c b/backend/canon_dr.c index 0ddedec63..6e34175ed 100644 --- a/backend/canon_dr.c +++ b/backend/canon_dr.c @@ -90,6 +90,18 @@ - improve sense_handler output - enable counter option - drop unused code + v7 2008-11-29, MAN + - jpeg support (size rounding and header overwrite) + - call object_position(load) between pages even if buffering is on + - use request sense info bytes on short scsi reads + - byte swap color BGR to RGB + - round image width down, not up + - round image height down to even # of lines + - always transfer even # of lines per block + - scsi and jpeg don't require reading extra lines to reach EOF + - rename buffer option to buffermode to avoid conflict with scanimage + - send ssm_do and ssm_df during sane_start + - improve sense_handler output SANE FLOW DIAGRAM @@ -150,7 +162,7 @@ #include "canon_dr.h" #define DEBUG 1 -#define BUILD 6 +#define BUILD 7 /* values for SANE_DEBUG_CANON_DR env var: - errors 5 @@ -954,6 +966,7 @@ init_user (struct scanner *s) s->br_y = s->page_height; s->threshold = 0x80; + s->compress_arg = 50; DBG (10, "init_user: finish\n"); @@ -1677,9 +1690,9 @@ sane_get_option_descriptor (SANE_Handle handle, SANE_Int option) } /*buffer mode*/ - if(option==OPT_BUFFER){ - opt->name = "buffer"; - opt->title = "Buffer"; + if(option==OPT_BUFFERMODE){ + opt->name = "buffermode"; + opt->title = "Buffer mode"; opt->desc = "Request scanner to read pages quickly from ADF into internal memory"; opt->type = SANE_TYPE_BOOL; if (s->has_buffer) @@ -1929,8 +1942,8 @@ sane_control_option (SANE_Handle handle, SANE_Int option, } return SANE_STATUS_GOOD; - case OPT_BUFFER: - *val_p = s->buffer; + case OPT_BUFFERMODE: + *val_p = s->buffermode; return SANE_STATUS_GOOD; /* Sensor Group */ @@ -2169,8 +2182,8 @@ sane_control_option (SANE_Handle handle, SANE_Int option, s->dropout_color_b = COLOR_EN_BLUE; return ssm_do(s); - case OPT_BUFFER: - s->buffer = val_c; + case OPT_BUFFERMODE: + s->buffermode = val_c; return ssm_buffer(s); /* Sensor Group */ @@ -2207,7 +2220,7 @@ ssm_buffer (struct scanner *s) if(s->source == SOURCE_ADF_DUPLEX){ set_SSM_BUFF_duplex(out, 0x02); } - if(s->buffer){ + if(s->buffermode){ set_SSM_BUFF_async(out, 0x40); } @@ -2481,52 +2494,53 @@ sane_get_parameters (SANE_Handle handle, SANE_Parameters * params) /* not started? get param data from user settings */ else { - int offset = 0; - DBG (15, "sane_get_parameters: not started, updating\n"); /* this backend only sends single frame images */ params->last_frame = 1; - while(1){ - - params->pixels_per_line - = s->resolution_x * (s->br_x - s->tl_x) / 1200 + offset; + params->pixels_per_line + = s->resolution_x * (s->br_x - s->tl_x) / 1200; - params->lines = s->resolution_y * (s->br_y - s->tl_y) / 1200; - - if (s->mode == MODE_COLOR) { - params->format = SANE_FRAME_RGB; - params->depth = 8; - params->bytes_per_line = params->pixels_per_line * 3; - if(s->compress == COMP_JPEG){ - params->format = SANE_FRAME_JPEG; - } - } - else if (s->mode == MODE_GRAYSCALE) { - params->format = SANE_FRAME_GRAY; - params->depth = 8; - params->bytes_per_line = params->pixels_per_line; - if(s->compress == COMP_JPEG){ - params->format = SANE_FRAME_JPEG; - } - } - else { - params->format = SANE_FRAME_GRAY; - params->depth = 1; - /* round down to byte boundary */ - params->pixels_per_line -= params->pixels_per_line % 8; - params->bytes_per_line = params->pixels_per_line / 8; - } + params->lines = s->resolution_y * (s->br_y - s->tl_y) / 1200; - /*scanners require even bytes per line*/ - if(params->bytes_per_line % 2){ - offset++; - } - else{ - break; - } - } + /* round lines down to even number */ + params->lines -= params->lines % 2; + + if (s->mode == MODE_COLOR) { + params->format = SANE_FRAME_RGB; + params->depth = 8; + + /* jpeg requires 8x8 squares */ + if(s->compress == COMP_JPEG){ + params->format = SANE_FRAME_JPEG; + params->pixels_per_line -= params->pixels_per_line % 8; + params->lines -= params->lines % 8; + } + + params->bytes_per_line = params->pixels_per_line * 3; + } + else if (s->mode == MODE_GRAYSCALE) { + params->format = SANE_FRAME_GRAY; + params->depth = 8; + + /* jpeg requires 8x8 squares */ + if(s->compress == COMP_JPEG){ + params->format = SANE_FRAME_JPEG; + params->pixels_per_line -= params->pixels_per_line % 8; + params->lines -= params->lines % 8; + } + + params->bytes_per_line = params->pixels_per_line; + } + else { + params->format = SANE_FRAME_GRAY; + params->depth = 1; + + /* round down to byte boundary */ + params->pixels_per_line -= params->pixels_per_line % 8; + params->bytes_per_line = params->pixels_per_line / 8; + } } DBG(15,"sane_get_parameters: x: max=%d, page=%d, gpw=%d, res=%d\n", @@ -2631,6 +2645,18 @@ sane_start (SANE_Handle handle) DBG (5, "sane_start: ERROR: cannot ssm buffer\n"); return ret; } + + ret = ssm_do(s); + if (ret != SANE_STATUS_GOOD) { + DBG (5, "sane_start: ERROR: cannot ssm do\n"); + return ret; + } + + ret = ssm_df(s); + if (ret != SANE_STATUS_GOOD) { + DBG (5, "sane_start: ERROR: cannot ssm df\n"); + return ret; + } } /* if already running, duplex needs to switch sides */ else if(s->source == SOURCE_ADF_DUPLEX){ @@ -2644,6 +2670,13 @@ sane_start (SANE_Handle handle) /* dont call object pos or scan on back side of duplex scan */ if(s->side == SIDE_FRONT || s->source == SOURCE_ADF_BACK){ + ret = object_position (s, SANE_TRUE); + if (ret != SANE_STATUS_GOOD) { + DBG (5, "sane_start: ERROR: cannot load page\n"); + s->started=0; + return ret; + } + s->bytes_rx[0]=0; s->bytes_rx[1]=0; s->lines_rx[0]=0; @@ -2679,15 +2712,7 @@ sane_start (SANE_Handle handle) /* first page of batch or user wants unbuffered scans */ /* send scan command */ - if(!s->started || !s->buffer){ - - ret = object_position (s, SANE_TRUE); - if (ret != SANE_STATUS_GOOD) { - DBG (5, "sane_start: ERROR: cannot load page\n"); - s->started=0; - return ret; - } - + if(!s->started || !s->buffermode){ ret = start_scan (s); if (ret != SANE_STATUS_GOOD) { DBG (5, "sane_start: ERROR: cannot start_scan\n"); @@ -2699,6 +2724,10 @@ sane_start (SANE_Handle handle) s->started=1; } + /* reset jpeg params on each page */ + s->jpeg_stage=JPEG_STAGE_NONE; + s->jpeg_ff_offset=0; + DBG (15, "started=%d, side=%d, source=%d\n", s->started, s->side, s->source); /* check if user cancelled during this start */ @@ -3070,7 +3099,12 @@ sane_read (SANE_Handle handle, SANE_Byte * buf, SANE_Int max_len, SANE_Int * len /* we have finished reading, clean up */ /* grab a bit more so scanner will send eof */ - if(s->bytes_rx[s->side] == s->bytes_tot[s->side]){ + if(s->connection == CONNECTION_USB + && s->bytes_rx[s->side] == s->bytes_tot[s->side] + && (s->compress != COMP_JPEG + || s->mode == MODE_HALFTONE + || s->mode == MODE_LINEART) + ){ read_from_scanner(s, s->side); } } @@ -3103,6 +3137,7 @@ read_from_scanner(struct scanner *s, int side) int bytes = s->buffer_size; int remain = s->bytes_tot[side] - s->bytes_rx[side]; int extra = 0; + size_t i; DBG (10, "read_from_scanner: start\n"); @@ -3114,6 +3149,11 @@ read_from_scanner(struct scanner *s, int side) /* all requests must end on line boundary */ bytes -= (bytes % s->params.bytes_per_line); + /* some larger scanners require even bytes per block */ + if(bytes % 2){ + bytes -= s->params.bytes_per_line; + } + /* these machines always send 1 extra line, and they need to send EOF too * so we ask for two full lines extra, and throw them away */ if(!bytes){ @@ -3161,6 +3201,62 @@ read_from_scanner(struct scanner *s, int side) inLen = 0; } + /* this is jpeg data, and we are near the beginning */ + /* look for the SOF header, and fix the image size */ + if(s->compress == COMP_JPEG + && (s->mode == MODE_GRAYSCALE || s->mode == MODE_COLOR) + && (s->jpeg_stage == JPEG_STAGE_NONE || s->jpeg_ff_offset < 0x0d) + ){ + for(i=0;ijpeg_stage == JPEG_STAGE_NONE && in[i] == 0xff){ + s->jpeg_ff_offset=0; + continue; + } + + s->jpeg_ff_offset++; + + /* last byte was an ff, this byte is SOF */ + if(s->jpeg_ff_offset == 1 && in[i] == 0xc0){ + s->jpeg_stage = JPEG_STAGE_SOF; + continue; + } + + if(s->jpeg_stage == JPEG_STAGE_SOF){ + + /* lines in start of frame, overwrite it */ + if(s->jpeg_ff_offset == 5){ + in[i] = (s->params.lines >> 8) & 0xff; + continue; + } + if(s->jpeg_ff_offset == 6){ + in[i] = s->params.lines & 0xff; + continue; + } + + /* width in start of frame, overwrite it */ + if(s->jpeg_ff_offset == 7){ + in[i] = (s->params.pixels_per_line >> 8) & 0xff; + continue; + } + if(s->jpeg_ff_offset == 8){ + in[i] = s->params.pixels_per_line & 0xff; + continue; + } + } + } + } + /* non-jpeg color, swap bgr to rgb, assumes full lines */ + else if (s->mode == MODE_COLOR){ + unsigned char temp; + for (i=0; i < inLen/3; i++){ + temp = in[i*3]; + in[i*3] = in[i*3+2]; + in[i*3+2] = temp; + } + } + if(inLen && !extra){ copy_buffer (s, in, inLen, side); } @@ -3413,131 +3509,44 @@ sense_handler (int fd, unsigned char * sensed_data, void *arg) DBG (5, "Sense=%#02x, ASC=%#02x, ASCQ=%#02x, EOM=%d, ILI=%d, info=%#08x\n", sense, asc, ascq, eom, ili, info); switch (sense) { - case 0x0: - if (0x80 == asc) { - DBG (5, "No sense: hardware status bits?\n"); - return SANE_STATUS_GOOD; - } - if (0x00 != asc) { - DBG (5, "No sense: unknown asc\n"); - return SANE_STATUS_IO_ERROR; - } - if (0x00 != ascq) { - DBG (5, "No sense: unknown ascq\n"); - return SANE_STATUS_IO_ERROR; - } + case 0: if (ili == 1) { s->rs_info = get_RS_information (sensed_data); DBG (5, "No sense: EOM remainder:%lu\n",(unsigned long)s->rs_info); return SANE_STATUS_EOF; } - DBG (5, "No sense: ready\n"); + DBG (5, "No sense: unknown asc/ascq\n"); return SANE_STATUS_GOOD; - case 0x2: - if (0x00 != asc) { - DBG (5, "Not ready: unknown asc\n"); - return SANE_STATUS_IO_ERROR; - } - if (0x00 != ascq) { - DBG (5, "Not ready: unknown ascq\n"); - return SANE_STATUS_IO_ERROR; - } - DBG (5, "Not ready: busy\n"); - return SANE_STATUS_DEVICE_BUSY; - break; + case 1: + DBG (5, "Recovered error: unknown asc/ascq\n"); + return SANE_STATUS_GOOD; - case 0x3: + case 2: + DBG (5, "Not ready: unknown asc/ascq\n"); + return SANE_STATUS_DEVICE_BUSY; + + case 3: if (asc == 0x3a && ascq == 0) { DBG (5, "Medium error: hopper empty\n"); return SANE_STATUS_NO_DOCS; } - if (0x80 != asc) { - DBG (5, "Medium error: unknown asc\n"); - return SANE_STATUS_IO_ERROR; - } - if (0x01 == ascq) { + if (asc == 0x81 && ascq == 1) { DBG (5, "Medium error: paper jam\n"); return SANE_STATUS_JAMMED; } - if (0x02 == ascq) { + if (asc == 0x80 && (ascq == 0 || ascq == 1)) { DBG (5, "Medium error: cover open\n"); return SANE_STATUS_COVER_OPEN; } - if (0x03 == ascq) { - DBG (5, "Medium error: hopper empty\n"); - return SANE_STATUS_NO_DOCS; - } - if (0x04 == ascq) { - DBG (5, "Medium error: unusual paper\n"); - return SANE_STATUS_JAMMED; - } - if (0x07 == ascq) { - DBG (5, "Medium error: double feed\n"); - return SANE_STATUS_JAMMED; - } - if (0x10 == ascq) { - DBG (5, "Medium error: no ink cartridge\n"); - return SANE_STATUS_IO_ERROR; - } - if (0x13 == ascq) { - DBG (5, "Medium error: temporary no data\n"); - return SANE_STATUS_DEVICE_BUSY; - } - if (0x14 == ascq) { - DBG (5, "Medium error: endorser error\n"); - return SANE_STATUS_IO_ERROR; - } - DBG (5, "Medium error: unknown ascq\n"); + DBG (5, "Medium error: unknown asc/ascq\n"); return SANE_STATUS_IO_ERROR; - break; - case 0x4: - if (0x80 != asc && 0x44 != asc && 0x47 != asc) { - DBG (5, "Hardware error: unknown asc\n"); - return SANE_STATUS_IO_ERROR; - } - if ((0x44 == asc) && (0x00 == ascq)) { - DBG (5, "Hardware error: EEPROM error\n"); - return SANE_STATUS_IO_ERROR; - } - if ((0x80 == asc) && (0x01 == ascq)) { - DBG (5, "Hardware error: FB motor fuse\n"); - return SANE_STATUS_IO_ERROR; - } - if ((0x80 == asc) && (0x02 == ascq)) { - DBG (5, "Hardware error: heater fuse\n"); - return SANE_STATUS_IO_ERROR; - } - if ((0x80 == asc) && (0x04 == ascq)) { - DBG (5, "Hardware error: ADF motor fuse\n"); - return SANE_STATUS_IO_ERROR; - } - if ((0x80 == asc) && (0x05 == ascq)) { - DBG (5, "Hardware error: mechanical error\n"); - return SANE_STATUS_IO_ERROR; - } - if ((0x80 == asc) && (0x06 == ascq)) { - DBG (5, "Hardware error: optical error\n"); - return SANE_STATUS_IO_ERROR; - } - if ((0x80 == asc) && (0x07 == ascq)) { - DBG (5, "Hardware error: Fan error\n"); - return SANE_STATUS_IO_ERROR; - } - if ((0x80 == asc) && (0x08 == ascq)) { - DBG (5, "Hardware error: IPC option error\n"); - return SANE_STATUS_IO_ERROR; - } - if ((0x80 == asc) && (0x10 == ascq)) { - DBG (5, "Hardware error: endorser error\n"); - return SANE_STATUS_IO_ERROR; - } + case 4: DBG (5, "Hardware error: unknown asc/ascq\n"); return SANE_STATUS_IO_ERROR; - break; - case 0x5: + case 5: if ((0x00 == asc) && (0x00 == ascq)) { DBG (5, "Illegal request: paper edge detected too soon\n"); return SANE_STATUS_INVAL; @@ -3578,7 +3587,7 @@ sense_handler (int fd, unsigned char * sensed_data, void *arg) return SANE_STATUS_IO_ERROR; break; - case 0x6: + case 6: if ((0x00 == asc) && (0x00 == ascq)) { DBG (5, "Unit attention: device reset\n"); return SANE_STATUS_GOOD; @@ -3591,6 +3600,22 @@ sense_handler (int fd, unsigned char * sensed_data, void *arg) return SANE_STATUS_IO_ERROR; break; + case 7: + DBG (5, "Data protect: unknown asc/ascq\n"); + return SANE_STATUS_IO_ERROR; + + case 8: + DBG (5, "Blank check: unknown asc/ascq\n"); + return SANE_STATUS_IO_ERROR; + + case 9: + DBG (5, "Vendor defined: unknown asc/ascq\n"); + return SANE_STATUS_IO_ERROR; + + case 0xa: + DBG (5, "Copy aborted: unknown asc/ascq\n"); + return SANE_STATUS_IO_ERROR; + case 0xb: if ((0x43 == asc) && (0x00 == ascq)) { DBG (5, "Aborted command: message error\n"); @@ -3624,6 +3649,18 @@ sense_handler (int fd, unsigned char * sensed_data, void *arg) return SANE_STATUS_IO_ERROR; break; + case 0xc: + DBG (5, "Equal: unknown asc/ascq\n"); + return SANE_STATUS_IO_ERROR; + + case 0xd: + DBG (5, "Volume overflow: unknown asc/ascq\n"); + return SANE_STATUS_IO_ERROR; + + case 0xe: + DBG (5, "Miscompare: unknown asc/ascq\n"); + return SANE_STATUS_IO_ERROR; + default: DBG (5, "Unknown Sense Code\n"); return SANE_STATUS_IO_ERROR; @@ -3697,8 +3734,11 @@ do_scsi_cmd(struct scanner *s, int runRS, int shortTime, return ret; } - /* FIXME: should we look at s->rs_info here? */ if (inBuff && inLen){ + if(ret == SANE_STATUS_EOF){ + DBG(25, "in: short read, remainder %d bytes\n", s->rs_info); + *inLen -= s->rs_info; + } hexdump(30, "in: <<", inBuff, *inLen); DBG(25, "in: read %d bytes\n", (int)*inLen); } diff --git a/backend/canon_dr.h b/backend/canon_dr.h index 759bd4d1f..aa9ab5b6e 100644 --- a/backend/canon_dr.h +++ b/backend/canon_dr.h @@ -62,7 +62,7 @@ enum scanner_Option OPT_DF_LENGTH, OPT_DROPOUT_COLOR_F, OPT_DROPOUT_COLOR_B, - OPT_BUFFER, + OPT_BUFFERMODE, /*sensor group*/ OPT_SENSOR_GROUP, @@ -239,7 +239,7 @@ struct scanner int df_thickness; int dropout_color_f; int dropout_color_b; - int buffer; + int buffermode; /* --------------------------------------------------------------------- */ /* values which are derived from setting the options above */ @@ -261,6 +261,8 @@ struct scanner int reading; int cancelled; int side; + int jpeg_stage; + int jpeg_ff_offset; /* total to read/write */ int bytes_tot[2]; @@ -296,6 +298,9 @@ struct scanner #define COMP_NONE WD_cmp_NONE #define COMP_JPEG WD_cmp_JPEG +#define JPEG_STAGE_NONE 0 +#define JPEG_STAGE_SOF 1 + /* these are same as scsi data to make code easier */ #define MODE_LINEART WD_comp_LA #define MODE_HALFTONE WD_comp_HT diff --git a/doc/descriptions/canon_dr.desc b/doc/descriptions/canon_dr.desc index 3bbe27f82..8f8042091 100644 --- a/doc/descriptions/canon_dr.desc +++ b/doc/descriptions/canon_dr.desc @@ -11,7 +11,7 @@ :backend "canon_dr" ; name of backend :url "http://www.thebility.com/canon/" -:version "6" ; version of backend +:version "7" ; version of backend :manpage "sane-canon_dr" ; name of manpage (if it exists) :comment "New backend as of SANE release 1.1.0, testers needed, see manpage" :devicetype :scanner ; start of a list of devices.... @@ -83,13 +83,13 @@ :interface "USB SCSI" :usbid "0x04a9" "0x160b" :status :good -:comment "Jpeg output broken, simplex/duplex/dropout-color working, multi-feed untested, imprinter unsupported" +:comment "Simplex, duplex, dropout-color, multifeed detection and JPEG working, imprinter unsupported" :model "DR-9080C" :interface "USB SCSI" :usbid "0x04a9" "0x1603" :status :good -:comment "Jpeg output broken, simplex/duplex/dropout-color working, multi-feed untested, imprinter unsupported" +:comment "Simplex, duplex, dropout-color, multifeed detection and JPEG working, imprinter unsupported" :model "DR-X10C" :interface "USB SCSI"