diff --git a/ChangeLog b/ChangeLog index 15c61d83e..24d4ba336 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,12 @@ +2009-03-23 m. allan noah + * backend/canon_dr.[ch], backend/canon_dr-cmd.h: backend v20 + - improved macros for inquiry and set window + - shorten inquiry vpd length to match windows driver + - remove status-length config option + - add padded-read config option + - rewrite do_usb_cmd to pad reads and calloc/copy buffers + * backend/canon_dr.conf.in: s/status-length/padded-read/g + 2009-03-22 m. allan noah * backend/canon_dr.[ch]: backend v19 - pad gray deinterlacing area for DR-2510C diff --git a/backend/canon_dr-cmd.h b/backend/canon_dr-cmd.h index e6b59b86c..1eb44d8df 100644 --- a/backend/canon_dr-cmd.h +++ b/backend/canon_dr-cmd.h @@ -11,8 +11,7 @@ #define USB_HEADER_LEN 12 #define USB_COMMAND_LEN 12 -#define USB_STATUS_LEN_MAX 32 -#define USB_STATUS_OFFSET 0 +#define USB_STATUS_LEN 4 #define USB_COMMAND_TIME 30000 #define USB_DATA_TIME 30000 #define USB_STATUS_TIME 30000 @@ -110,12 +109,11 @@ putnbyte (unsigned char *pnt, unsigned int value, unsigned int nbytes) #define INQUIRY_len 6 #define INQUIRY_std_len 0x30 -#define INQUIRY_vpd_len 0x28 +#define INQUIRY_vpd_len 0x1e #define set_IN_evpd(icb, val) setbitfield(icb + 1, 1, 0, val) #define set_IN_page_code(icb, val) icb[0x02]=val #define set_IN_return_size(icb,val) icb[0x04]=val -#define set_IN_length(out,n) out[0x04]=n-5 #define get_IN_periph_qual(in) getbitfield(in, 0x07, 5) #define IN_periph_qual_lun 0x00 @@ -157,10 +155,10 @@ putnbyte (unsigned char *pnt, unsigned int value, unsigned int nbytes) #define get_IN_std_res_1200(in) getbitfield(in+ 0x13, 1, 0) #define get_IN_window_width(in) getnbyte(in + 0x14, 4) #define get_IN_window_length(in) getnbyte(in + 0x18, 4) -#define get_IN_unknown7(in) getbitfield(in+0x1c, 1, 7) -#define get_IN_unknown6(in) getbitfield(in+0x1c, 1, 6) -#define get_IN_unknown5(in) getbitfield(in+0x1c, 1, 5) -#define get_IN_unknown4(in) getbitfield(in+0x1c, 1, 4) +#define get_IN_awd(in) getbitfield(in+0x1c, 1, 7) +#define get_IN_ce_emphasis(in) getbitfield(in+0x1c, 1, 6) +#define get_IN_c_emphasis(in) getbitfield(in+0x1c, 1, 5) +#define get_IN_high_quality(in) getbitfield(in+0x1c, 1, 4) #define get_IN_multilevel(in) getbitfield(in+0x1c, 1, 3) #define get_IN_half_tone(in) getbitfield(in+0x1c, 1, 2) #define get_IN_monochrome(in) getbitfield(in+0x1c, 1, 1) @@ -427,8 +425,10 @@ putnbyte (unsigned char *pnt, unsigned int value, unsigned int nbytes) /* 0x1d - Reverse image, reserved area, padding type */ #define set_WD_rif(sb, val) setbitfield(sb + 0x1d, 1, 7, val) #define get_WD_rif(sb) getbitfield(sb + 0x1d, 1, 7) -#define set_WD_reserved(sb, val) sb[0x1d] = val -#define get_WD_reserved(sb) sb[0x1d] +#define set_WD_rgb(sb, val) setbitfield(sb + 0x1d, 7, 6, val) +#define get_WD_rgb(sb) getbitfield(sb + 0x1d, 7, 6) +#define set_WD_padding(sb, val) setbitfield(sb + 0x1d, 7, 0, val) +#define get_WD_padding(sb) getbitfield(sb + 0x1d, 7, 0) /* 0x1e,0x1f - Bit ordering */ #define set_WD_bitorder(sb, val) putnbyte(sb + 0x1e, val, 2) diff --git a/backend/canon_dr.c b/backend/canon_dr.c index 9175ad18a..9019ccc39 100644 --- a/backend/canon_dr.c +++ b/backend/canon_dr.c @@ -150,6 +150,12 @@ v19 2009-03-22, MAN - pad gray deinterlacing area for DR-2510C - override tl_x and br_x for fixed width scanners + v20 2009-03-23, MAN + - improved macros for inquiry and set window + - shorten inquiry vpd length to match windows driver + - remove status-length config option + - add padded-read config option + - rewrite do_usb_cmd to pad reads and calloc/copy buffers SANE FLOW DIAGRAM @@ -210,7 +216,7 @@ #include "canon_dr.h" #define DEBUG 1 -#define BUILD 19 +#define BUILD 20 /* values for SANE_DEBUG_CANON_DR env var: - errors 5 @@ -259,8 +265,8 @@ static const char string_Back[] = "Back"; /* Also set via config file. */ static int global_buffer_size; static int global_buffer_size_default = 64 * 1024; -static int global_status_length; -static int global_status_length_default = 4; +static int global_padded_read; +static int global_padded_read_default = 0; static char global_vendor_name[9]; static char global_model_name[17]; static char global_version_name[5]; @@ -409,29 +415,29 @@ sane_get_devices (const SANE_Device *** device_list, SANE_Bool local_only) global_buffer_size = buf; } - /* STATUS: we clamp from 1 to 32 */ - else if (!strncmp (lp, "status-length", 13) && isspace (lp[13])) { + /* PADDED READ: we clamp to 0 or 1 */ + else if (!strncmp (lp, "padded-read", 11) && isspace (lp[11])) { int buf; - lp += 13; + lp += 11; lp = sanei_config_skip_whitespace (lp); buf = atoi (lp); - if (buf < 1) { - DBG (5, "sane_get_devices: config option \"status-length\" " - "(%d) is < 1, ignoring!\n", buf); + if (buf < 0) { + DBG (5, "sane_get_devices: config option \"padded-read\" " + "(%d) is < 0, ignoring!\n", buf); continue; } - if (buf > 32) { - DBG (5, "sane_get_devices: config option \"status-length\" " - "(%d) is > 32, ignoring!\n", buf); + if (buf > 1) { + DBG (5, "sane_get_devices: config option \"padded-read\" " + "(%d) is > 1, ignoring!\n", buf); } - DBG (15, "sane_get_devices: setting \"status-length\" " - "to %d\n", buf); + DBG (15, "sane_get_devices: setting \"padded-read\" to %d\n", + buf); - global_status_length = buf; + global_padded_read = buf; } /* VENDOR: we ingest up to 8 bytes */ @@ -597,7 +603,7 @@ attach_one (const char *device_name, int connType) /* config file settings */ s->buffer_size = global_buffer_size; - s->status_length = global_status_length; + s->padded_read = global_padded_read; /* copy the device name */ strcpy (s->device_name, device_name); @@ -958,10 +964,10 @@ init_vpd (struct scanner *s) s->max_y_basic = get_IN_window_length(in); DBG(15, " max length: %2.2f inches\n",(float)s->max_y_basic/s->basic_y_res); - DBG (15, " unknown7: %d\n", get_IN_unknown7(in)); - DBG (15, " unknown6: %d\n", get_IN_unknown6(in)); - DBG (15, " unknown5: %d\n", get_IN_unknown5(in)); - DBG (15, " unknown4: %d\n", get_IN_unknown4(in)); + DBG (15, " AWD: %d\n", get_IN_awd(in)); + DBG (15, " CE Emphasis: %d\n", get_IN_ce_emphasis(in)); + DBG (15, " C Emphasis: %d\n", get_IN_c_emphasis(in)); + DBG (15, " High quality: %d\n", get_IN_high_quality(in)); /* known modes */ s->can_grayscale = get_IN_multilevel (in); @@ -1035,24 +1041,20 @@ init_model (struct scanner *s) } /* specific settings missing from vpd */ - if (strstr (s->model_name,"DR-9080")){ + if (strstr (s->model_name,"DR-9080") + || strstr (s->model_name,"DR-7580")){ s->has_comp_JPEG = 1; - s->unknown_byte = 0x20; - } - - else if (strstr (s->model_name,"DR-7580")){ - s->has_comp_JPEG = 1; - s->unknown_byte = 0x20; + s->rgb_format = 2; } else if (strstr (s->model_name,"DR-2580")){ s->invert_tly = 1; - s->unknown_byte = 0x10; + s->rgb_format = 1; s->has_counter = 1; } else if (strstr (s->model_name,"DR-2510")){ - s->unknown_byte = 0x10; + s->rgb_format = 1; s->unknown_byte2 = 0x80; s->has_counter = 1; s->head_interlace = HEAD_INTERLACE_2510; @@ -3145,9 +3147,10 @@ set_window (struct scanner *s) } set_WD_rif (desc1, s->rif); + set_WD_rgb(desc1, s->rgb_format); + set_WD_padding(desc1, s->padding); /*FIXME: what is this? */ - set_WD_reserved(desc1, s->unknown_byte); set_WD_reserved2(desc1, s->unknown_byte2); set_WD_compress_type(desc1, COMP_NONE); @@ -3996,7 +3999,7 @@ static void default_globals(void) { global_buffer_size = global_buffer_size_default; - global_status_length = global_status_length_default; + global_padded_read = global_padded_read_default; global_vendor_name[0] = 0; global_model_name[0] = 0; global_version_name[0] = 0; @@ -4330,164 +4333,216 @@ do_usb_cmd(struct scanner *s, int runRS, int shortTime, unsigned char * inBuff, size_t * inLen ) { - /*sanei_usb overwrites the transfer size, - * so make some local copies */ - size_t usb_cmdLen = USB_HEADER_LEN + USB_COMMAND_LEN; - size_t usb_outLen = USB_HEADER_LEN + outLen; - size_t usb_statLen = s->status_length; - size_t askLen = 0; - - /*copy the callers buffs into larger, padded ones*/ - unsigned char usb_cmdBuff[USB_HEADER_LEN + USB_COMMAND_LEN]; - unsigned char * usb_outBuff; - unsigned char usb_statBuff[USB_STATUS_LEN_MAX]; - - int cmdTime = USB_COMMAND_TIME; - int outTime = USB_DATA_TIME; - int inTime = USB_DATA_TIME; - int statTime = USB_STATUS_TIME; + size_t offset; + size_t length; + size_t actual; + unsigned char * buffer; + int timeout; int ret = 0; int ret2 = 0; DBG (10, "do_usb_cmd: start\n"); - if(shortTime){ - cmdTime = USB_COMMAND_TIME/60; - outTime = USB_DATA_TIME/60; - inTime = USB_DATA_TIME/60; - statTime = USB_STATUS_TIME/60; - } + /****************************************************************/ + /* the command stage */ + { + offset = USB_HEADER_LEN; + length = offset+USB_COMMAND_LEN; + actual = length; + timeout = USB_COMMAND_TIME; - /* build a USB packet around the SCSI command */ - memset(&usb_cmdBuff,0,usb_cmdLen); - usb_cmdBuff[3] = usb_cmdLen-4; - usb_cmdBuff[5] = 1; - usb_cmdBuff[6] = 0x90; - memcpy(usb_cmdBuff+USB_HEADER_LEN,cmdBuff,cmdLen); + /* change timeout */ + if(shortTime) + timeout/=60; + sanei_usb_set_timeout(timeout); - /* change timeout */ - sanei_usb_set_timeout(cmdTime); - - /* write the command out */ - DBG(25, "cmd: writing %d bytes, timeout %d\n", (int)usb_cmdLen, - cmdTime); - hexdump(30, "cmd: >>", usb_cmdBuff, usb_cmdLen); - ret = sanei_usb_write_bulk(s->fd, usb_cmdBuff, &usb_cmdLen); - DBG(25, "cmd: wrote %d bytes, retVal %d\n", (int)usb_cmdLen, ret); - - if(ret == SANE_STATUS_EOF){ - DBG(5,"cmd: got EOF, returning IO_ERROR\n"); + /* build buffer */ + buffer = calloc(length,1); + if(!buffer){ + DBG(5,"cmd: no mem\n"); return SANE_STATUS_IO_ERROR; - } - if(ret != SANE_STATUS_GOOD){ - DBG(5,"cmd: return error '%s'\n",sane_strstatus(ret)); + } + + /* build a USB packet around the SCSI command */ + buffer[3] = length-4; + buffer[5] = 1; + buffer[6] = 0x90; + memcpy(buffer+offset,cmdBuff,cmdLen); + + /* write the command out */ + DBG(25, "cmd: writing %d bytes, timeout %d\n", (int)length, timeout); + hexdump(30, "cmd: >>", buffer, length); + ret = sanei_usb_write_bulk(s->fd, buffer, &actual); + DBG(25, "cmd: wrote %d bytes, retVal %d\n", (int)actual, ret); + + if(length != actual){ + DBG(5,"cmd: wrong size %d/%d\n", (int)length, (int)actual); + free(buffer); + return SANE_STATUS_IO_ERROR; + } + if(ret != SANE_STATUS_GOOD){ + DBG(5,"cmd: write error '%s'\n",sane_strstatus(ret)); + free(buffer); return ret; + } + free(buffer); } - if(usb_cmdLen != USB_HEADER_LEN + USB_COMMAND_LEN){ - DBG(5,"cmd: wrong size %d/%d\n", USB_COMMAND_LEN, (int)usb_cmdLen); + + /****************************************************************/ + /* the output stage */ + if(outBuff && outLen){ + + offset = USB_HEADER_LEN; + length = offset+outLen; + actual = length; + timeout = USB_DATA_TIME; + + /* change timeout */ + if(shortTime) + timeout/=60; + sanei_usb_set_timeout(timeout); + + /* build buffer */ + buffer = calloc(length,1); + if(!buffer){ + DBG(5,"out: no mem\n"); return SANE_STATUS_IO_ERROR; + } + + /* build a USB packet around the SCSI command */ + buffer[3] = length-4; + buffer[5] = 2; + buffer[6] = 0xb0; + memcpy(buffer+offset,outBuff,outLen); + + /* write the command out */ + DBG(25, "out: writing %d bytes, timeout %d\n", (int)length, timeout); + hexdump(30, "out: >>", buffer, length); + ret = sanei_usb_write_bulk(s->fd, buffer, &actual); + DBG(25, "out: wrote %d bytes, retVal %d\n", (int)actual, ret); + + if(length != actual){ + DBG(5,"out: wrong size %d/%d\n", (int)length, (int)actual); + free(buffer); + return SANE_STATUS_IO_ERROR; + } + if(ret != SANE_STATUS_GOOD){ + DBG(5,"out: write error '%s'\n",sane_strstatus(ret)); + free(buffer); + return ret; + } + free(buffer); } - /* this command has a write component, and a place to get it */ - if(outBuff && outLen && outTime){ + /****************************************************************/ + /* the input stage */ + if(inBuff && inLen){ - usb_outBuff = calloc(usb_outLen,1); - if(!usb_outBuff){ - DBG(5,"out: no mem\n"); - return SANE_STATUS_IO_ERROR; - } + offset = 0; + if(s->padded_read) + offset = USB_HEADER_LEN; - usb_outBuff[3] = usb_outLen-4; - usb_outBuff[5] = 2; - usb_outBuff[6] = 0xb0; - memcpy(usb_outBuff+USB_HEADER_LEN,outBuff,outLen); + length = offset+*inLen; + actual = length; + timeout = USB_DATA_TIME; - /* change timeout */ - sanei_usb_set_timeout(outTime); + /* change timeout */ + if(shortTime) + timeout/=60; + sanei_usb_set_timeout(timeout); - DBG(25, "out: writing %d bytes, timeout %d\n", (int)usb_outLen, outTime); - hexdump(30, "out: >>", usb_outBuff, usb_outLen); - ret = sanei_usb_write_bulk(s->fd, usb_outBuff, &usb_outLen); - DBG(25, "out: wrote %d bytes, retVal %d\n", (int)usb_outLen, ret); + /* build buffer */ + buffer = calloc(length,1); + if(!buffer){ + DBG(5,"in: no mem\n"); + return SANE_STATUS_IO_ERROR; + } + + DBG(25, "in: reading %d bytes, timeout %d\n", (int)length, timeout); + ret = sanei_usb_read_bulk(s->fd, buffer, &actual); + DBG(25, "in: read %d bytes, retval %d\n", (int)actual, ret); + hexdump(30, "in: <<", buffer, actual); - if(ret == SANE_STATUS_EOF){ - DBG(5,"out: got EOF, returning IO_ERROR\n"); - free(usb_outBuff); - return SANE_STATUS_IO_ERROR; - } - if(ret != SANE_STATUS_GOOD){ - DBG(5,"out: return error '%s'\n",sane_strstatus(ret)); - free(usb_outBuff); - return ret; - } - if(usb_outLen != outLen + USB_HEADER_LEN){ - DBG(5,"out: wrong size %d/%d\n", (int)outLen, (int)usb_outLen); - free(usb_outBuff); - return SANE_STATUS_IO_ERROR; - } - free(usb_outBuff); - } - - /* this command has a read component, and a place to put it */ - if(inBuff && inLen && inTime){ - - askLen = *inLen; - memset(inBuff,0,askLen); - - /* change timeout */ - sanei_usb_set_timeout(inTime); - - DBG(25, "in: reading %d bytes, timeout %d\n", (int)askLen, inTime); - - ret = sanei_usb_read_bulk(s->fd, inBuff, inLen); - DBG(25, "in: read %d bytes, retval %d\n", (int)*inLen, ret); - hexdump(30, "in: <<", inBuff, *inLen); - - if(!*inLen){ - DBG(5,"in: got no data, clearing\n"); - return do_usb_clear(s,runRS); - } - if(ret != SANE_STATUS_GOOD){ - DBG(5,"in: return error '%s'\n",sane_strstatus(ret)); - return ret; - } - if(*inLen != askLen){ - ret = SANE_STATUS_EOF; - DBG(5,"in: wrong size, %d/%d\n", (int)*inLen,(int)askLen); - } - } - - /*gather the scsi status byte. use ret2 instead of ret for status*/ - memset(&usb_statBuff,0,USB_STATUS_LEN_MAX); - - /* change timeout */ - sanei_usb_set_timeout(statTime); - - DBG(25, "stat: reading %d bytes, timeout %d\n", (int)usb_statLen, statTime); - ret2 = sanei_usb_read_bulk(s->fd, usb_statBuff, &usb_statLen); - DBG(25, "stat: read %d bytes, retVal %d\n", (int)usb_statLen, ret2); - hexdump(30, "stat: <<", usb_statBuff, usb_statLen); - - if(!usb_statLen){ - DBG(5,"stat: got no data, clearing\n"); + if(!actual){ + *inLen = 0; + DBG(5,"in: got no data, clearing\n"); + free(buffer); return do_usb_clear(s,runRS); - } - if(ret2 != SANE_STATUS_GOOD){ - DBG(5,"stat: return error '%s'\n",sane_strstatus(ret2)); - return ret2; - } - if(usb_statLen != (size_t)s->status_length){ - DBG(5,"stat: wrong size %d/%d\n", s->status_length, (int)usb_statLen); + } + if(actual < offset){ + *inLen = 0; + DBG(5,"in: read shorter than offset\n"); + free(buffer); return SANE_STATUS_IO_ERROR; + } + if(ret != SANE_STATUS_GOOD){ + *inLen = 0; + DBG(5,"in: return error '%s'\n",sane_strstatus(ret)); + free(buffer); + return ret; + } + + if(length != actual){ + ret = SANE_STATUS_EOF; + DBG(5,"in: short read, %d/%d\n", (int)length,(int)actual); + } + + /* ignore the USB packet around the SCSI command */ + *inLen = actual - offset; + memcpy(inBuff,buffer+offset,*inLen); + + free(buffer); } - /* FIXME: interpret the status fields - if(usb_statBuff[0] || usb_statBuff[1] || usb_statBuff[2] || usb_statBuff[3]){ - DBG(25,"stat: bad stat?\n"); - return SANE_STATUS_IO_ERROR; + /****************************************************************/ + /* the status stage */ + { + offset = 0; + if(s->padded_read) + offset = USB_HEADER_LEN; + + length = offset+USB_STATUS_LEN; + actual = length; + timeout = USB_STATUS_TIME; + + /* change timeout */ + if(shortTime) + timeout/=60; + sanei_usb_set_timeout(timeout); + + /* build buffer */ + buffer = calloc(length,1); + if(!buffer){ + DBG(5,"stat: no mem\n"); + return SANE_STATUS_IO_ERROR; + } + + DBG(25, "stat: reading %d bytes, timeout %d\n", (int)length, timeout); + ret2 = sanei_usb_read_bulk(s->fd, buffer, &actual); + DBG(25, "stat: read %d bytes, retval %d\n", (int)actual, ret2); + hexdump(30, "stat: <<", buffer, actual); + + if(!actual){ + DBG(5,"stat: got no data, clearing\n"); + free(buffer); + return do_usb_clear(s,runRS); + } + if(ret2 != SANE_STATUS_GOOD){ + DBG(5,"stat: return error '%s'\n",sane_strstatus(ret2)); + free(buffer); + return ret2; + } + if(length != actual){ + DBG(5,"stat: short read, %d/%d\n",(int)length,(int)actual); + free(buffer); + return SANE_STATUS_IO_ERROR; + } + + /*FIXME: inspect the status response?*/ + + free(buffer); } - */ DBG (10, "do_usb_cmd: finish\n"); diff --git a/backend/canon_dr.conf.in b/backend/canon_dr.conf.in index 71f2d9c02..83ae319d5 100644 --- a/backend/canon_dr.conf.in +++ b/backend/canon_dr.conf.in @@ -19,8 +19,8 @@ #option buffer-size 65536 ####################################################################### -# Most scanners use a 4 byte status, but some use 16 -#option status-length 4 +# Most scanners dont pad their reads +#option padded-read 0 ####################################################################### # SCSI scanners: @@ -46,10 +46,10 @@ scsi CANON DR # gmail dot com - with canon_dr in the subject line # DR-2080C (uses weird protocol) -option vendor-name CANON -option model-name DR-2080C -option version-name XXXX -option status-length 16 +option vendor-name CANON +option model-name DR-2080C +option version-name XXXX +option padded-read 1 usb 0x04a9 0x1601 # CR-180 @@ -74,10 +74,10 @@ usb 0x04a9 0x1608 usb 0x04a9 0x1609 # DR-2050C/SP (uses weird protocol) -option vendor-name CANON -option model-name DR-2050C -option version-name XXXX -option status-length 16 +option vendor-name CANON +option model-name DR-2050C +option version-name XXXX +option padded-read 1 usb 0x04a9 0x160a # DR-7580 diff --git a/backend/canon_dr.h b/backend/canon_dr.h index 87713b473..46f095b8e 100644 --- a/backend/canon_dr.h +++ b/backend/canon_dr.h @@ -143,12 +143,13 @@ struct scanner int has_back; /* not all duplex scanners can do adf back side only */ int has_comp_JPEG; int has_buffer; + int rgb_format; /* meaning unknown */ + int padding; /* meaning unknown */ int invert_tly; /* weird bug in some smaller scanners */ - int unknown_byte; /* weird byte, required, meaning unknown */ int unknown_byte2; /* weird byte, required, meaning unknown */ - size_t status_length; /* usually 4, sometimes 16 */ - int fixed_width; /* sometime machines always scan full width */ + int padded_read; /* some machines need extra 12 bytes on reads */ + int fixed_width; /* some machines always scan full width */ int color_interlace; /* different models interlace colors differently */ int duplex_interlace; /* different models interlace sides differently */