/* sane - Scanner Access Now Easy. Copyright (C) 2007 Philippe Rétornaz 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 backend is for HP LaserJet M1005 MFP Highly inspired from the epson backend */ #ifdef _AIX #include /* MUST come first for AIX! */ #endif #define BUILD 0 #include "../include/sane/config.h" #include #include #include #include #include #include #include #include #include #include #include "../include/sane/sanei_usb.h" #include "../include/sane/saneopts.h" #include "../include/sane/sanei_backend.h" #define BACKEND_NAME hpljm1005 #define MAGIC_NUMBER 0x41535001 #define PKT_READ_STATUS 0x0 #define PKT_UNKNOW_1 0x1 #define PKT_START_SCAN 0x2 #define PKT_GO_IDLE 0x3 #define PKT_DATA 0x5 #define PKT_READCONF 0x6 #define PKT_SETCONF 0x7 #define PKT_END_DATA 0xe #define PKT_RESET 0x15 #define RED_LAYER 0x3 #define GREEN_LAYER 0x4 #define BLUE_LAYER 0x5 #define GRAY_LAYER 0x6 struct usbdev_s { SANE_Int vendor_id; SANE_Int product_id; SANE_String_Const vendor_s; SANE_String_Const model_s; SANE_String_Const type_s; }; /* Zero-terminated USB VID/PID array */ static struct usbdev_s usbid[] = { { 0x03f0, 0x3b17, "Hewlett-Packard", "LaserJet M1005", "multi-function peripheral" }, { 0, 0, NULL, NULL, NULL}, { 0, 0, NULL, NULL, NULL} }; static int cur_idx; #define BR_CONT_MIN 0x1 #define BR_CONT_MAX 0xb #define RGB 1 #define GRAY 0 #define MAX_X_H 0x352 #define MAX_Y_H 0x490 #define MAX_X_S 220 #define MAX_Y_S 330 #define OPTION_MAX 9 static SANE_Word resolution_list[] = { 7,75,100,150,200,300,600,1200 }; static SANE_Range range_x = {0,MAX_X_S,0}; static SANE_Range range_y = {0,MAX_Y_S,0}; static SANE_Range range_br_cont = {BR_CONT_MIN,BR_CONT_MAX,0}; static const SANE_String_Const mode_list[] = { "Gray", "Color", NULL }; #define X1_OFFSET 2 #define X2_OFFSET 4 #define Y1_OFFSET 3 #define Y2_OFFSET 5 #define RES_OFFSET 1 #define COLOR_OFFSET 8 #define BRIGH_OFFSET 6 #define CONTR_OFFSET 7 #define STATUS_IDLE 0 #define STATUS_SCANNING 1 struct device_s { struct device_s * next; SANE_String_Const devname; int idx; /* Index in the usbid array */ int dn; /* Usb "Handle" */ SANE_Option_Descriptor optiond[OPTION_MAX]; char * buffer; int bufs; int read_offset; int write_offset_r; int write_offset_g; int write_offset_b; int status; int width; int height; SANE_Word optionw[OPTION_MAX]; uint32_t conf_data[512]; uint32_t packet_data[512]; }; static struct device_s *devlist_head; static int devlist_count; /* Number of element in the list */ /* * List of pointers to devices - will be dynamically allocated depending * on the number of devices found. */ static SANE_Device **devlist = NULL; static void update_img_size(struct device_s *dev) { /* HACK FIXME */ switch(dev->optionw[RES_OFFSET]) { case 75: dev->height = 880; dev->width = 640; break; case 100: dev->height = 1180; dev->width = 848; break; case 150: dev->height = 1775; dev->width = 1264; break; case 200: dev->width = 1696; dev->height = 2351; break; case 300: dev->width = 2528; dev->height = 3510; break; case 600: dev->width = 5088; dev->height = 7020; break; case 1200: dev->width = 10208; dev->height = 14025; break; } dev->bufs = dev->width * dev->height; if(dev->optionw[COLOR_OFFSET] == RGB) dev->bufs *= 3; } /* This function is copy/pasted from the Epson backend */ static size_t max_string_size (const SANE_String_Const strings[]) { size_t size, max_size = 0; int i; for (i = 0; strings[i]; i++) { size = strlen (strings[i]) + 1; if (size > max_size) max_size = size; } return max_size; } static SANE_Status attach(SANE_String_Const devname) { struct device_s * dev; dev = malloc(sizeof(struct device_s)); if(!dev) return SANE_STATUS_NO_MEM; memset(dev,0,sizeof(struct device_s)); dev->devname = devname; /* Init the whole structure with default values */ /* Number of options */ dev->optiond[0].name = ""; dev->optiond[0].title = NULL; dev->optiond[0].desc = NULL; dev->optiond[0].type = SANE_TYPE_INT; dev->optiond[0].unit = SANE_UNIT_NONE; dev->optiond[0].size = sizeof (SANE_Word); dev->optionw[0] = OPTION_MAX; /* resolution */ dev->optiond[RES_OFFSET].name = "resolution"; dev->optiond[RES_OFFSET].title = "resolution"; dev->optiond[RES_OFFSET].desc = "resolution"; dev->optiond[RES_OFFSET].type = SANE_TYPE_INT; dev->optiond[RES_OFFSET].unit = SANE_UNIT_DPI; dev->optiond[RES_OFFSET].type = SANE_TYPE_INT; dev->optiond[RES_OFFSET].size = sizeof (SANE_Word); dev->optiond[RES_OFFSET].cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT; dev->optiond[RES_OFFSET].constraint_type = SANE_CONSTRAINT_WORD_LIST; dev->optiond[RES_OFFSET].constraint.word_list = resolution_list; dev->optionw[RES_OFFSET] = 75; /* scan area */ dev->optiond[X1_OFFSET].name = "tl-x"; dev->optiond[X1_OFFSET].title = "tl-x"; dev->optiond[X1_OFFSET].desc = "tl-x"; dev->optiond[X1_OFFSET].type = SANE_TYPE_INT; dev->optiond[X1_OFFSET].unit = SANE_UNIT_MM; dev->optiond[X1_OFFSET].size = sizeof (SANE_Word); dev->optiond[X1_OFFSET].cap = /*SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT*/ 0; dev->optiond[X1_OFFSET].constraint_type = SANE_CONSTRAINT_RANGE; dev->optiond[X1_OFFSET].constraint.range = &range_x; dev->optionw[X1_OFFSET] = 0; dev->optiond[Y1_OFFSET].name = "tl-y"; dev->optiond[Y1_OFFSET].title = "tl-y"; dev->optiond[Y1_OFFSET].desc = "tl-y"; dev->optiond[Y1_OFFSET].type = SANE_TYPE_INT; dev->optiond[Y1_OFFSET].unit = SANE_UNIT_MM; dev->optiond[Y1_OFFSET].size = sizeof (SANE_Word); dev->optiond[Y1_OFFSET].cap = /*SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT*/ 0; dev->optiond[Y1_OFFSET].constraint_type = SANE_CONSTRAINT_RANGE; dev->optiond[Y1_OFFSET].constraint.range = &range_y; dev->optionw[Y1_OFFSET] = 0; dev->optiond[X2_OFFSET].name = "br-x"; dev->optiond[X2_OFFSET].title = "br-x"; dev->optiond[X2_OFFSET].desc = "br-x"; dev->optiond[X2_OFFSET].type = SANE_TYPE_INT; dev->optiond[X2_OFFSET].unit = SANE_UNIT_MM; dev->optiond[X2_OFFSET].size = sizeof (SANE_Word); dev->optiond[X2_OFFSET].cap = /*SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT*/ 0; dev->optiond[X2_OFFSET].constraint_type = SANE_CONSTRAINT_RANGE; dev->optiond[X2_OFFSET].constraint.range = &range_x; dev->optionw[X2_OFFSET] = MAX_X_S; dev->optiond[Y2_OFFSET].name = "br-y"; dev->optiond[Y2_OFFSET].title = "br-y"; dev->optiond[Y2_OFFSET].desc = "br-y"; dev->optiond[Y2_OFFSET].type = SANE_TYPE_INT; dev->optiond[Y2_OFFSET].unit = SANE_UNIT_MM; dev->optiond[Y2_OFFSET].size = sizeof (SANE_Word); dev->optiond[Y2_OFFSET].cap = /*SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT*/ 0; dev->optiond[Y2_OFFSET].constraint_type = SANE_CONSTRAINT_RANGE; dev->optiond[Y2_OFFSET].constraint.range = &range_y; dev->optionw[Y2_OFFSET] = MAX_Y_S; /* brightness */ dev->optiond[BRIGH_OFFSET].name = "brightness"; dev->optiond[BRIGH_OFFSET].title = "Brightness"; dev->optiond[BRIGH_OFFSET].desc = "Set the brightness"; dev->optiond[BRIGH_OFFSET].type = SANE_TYPE_INT; dev->optiond[BRIGH_OFFSET].unit = SANE_UNIT_NONE; dev->optiond[BRIGH_OFFSET].size = sizeof (SANE_Word); dev->optiond[BRIGH_OFFSET].cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT; dev->optiond[BRIGH_OFFSET].constraint_type = SANE_CONSTRAINT_RANGE; dev->optiond[BRIGH_OFFSET].constraint.range = &range_br_cont; dev->optionw[BRIGH_OFFSET] = 0x6; /* contrast */ dev->optiond[CONTR_OFFSET].name = "contrast"; dev->optiond[CONTR_OFFSET].title = "Contrast"; dev->optiond[CONTR_OFFSET].desc = "Set the contrast"; dev->optiond[CONTR_OFFSET].type = SANE_TYPE_INT; dev->optiond[CONTR_OFFSET].unit = SANE_UNIT_NONE; dev->optiond[CONTR_OFFSET].size = sizeof (SANE_Word); dev->optiond[CONTR_OFFSET].cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT; dev->optiond[CONTR_OFFSET].constraint_type = SANE_CONSTRAINT_RANGE; dev->optiond[CONTR_OFFSET].constraint.range = &range_br_cont; dev->optionw[CONTR_OFFSET] = 0x6; /* Color */ dev->optiond[COLOR_OFFSET].name = SANE_NAME_SCAN_MODE; dev->optiond[COLOR_OFFSET].title = SANE_TITLE_SCAN_MODE; dev->optiond[COLOR_OFFSET].desc = SANE_DESC_SCAN_MODE; dev->optiond[COLOR_OFFSET].type = SANE_TYPE_STRING; dev->optiond[COLOR_OFFSET].size = max_string_size (mode_list); dev->optiond[COLOR_OFFSET].cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT; dev->optiond[COLOR_OFFSET].constraint_type = SANE_CONSTRAINT_STRING_LIST; dev->optiond[COLOR_OFFSET].constraint.string_list = mode_list; dev->optionw[COLOR_OFFSET] = RGB; /* RGB */ dev->dn = 0; dev->idx = cur_idx; dev->status = STATUS_IDLE; dev->next = devlist_head; devlist_head = dev; devlist_count++; return SANE_STATUS_GOOD; } SANE_Status sane_init (SANE_Int * version_code, SANE_Auth_Callback __sane_unused__ authorize) { if (version_code != NULL) *version_code = SANE_VERSION_CODE (V_MAJOR, V_MINOR, BUILD); sanei_usb_init (); return SANE_STATUS_GOOD; } void sane_exit(void) { /* free everything */ struct device_s *iter; if(devlist) { int i; for(i = 0; devlist[i]; i++) free(devlist[i]); free(devlist); devlist = NULL; } if(devlist_head) { iter = devlist_head->next; free(devlist_head); devlist_head = NULL; while(iter) { struct device_s *tmp = iter; iter = iter->next; free(tmp); } } devlist_count = 0; } SANE_Status sane_get_devices (const SANE_Device * **device_list, SANE_Bool __sane_unused__ local_only) { struct device_s *iter; int i; devlist_count = 0; if(devlist_head) { iter = devlist_head->next; free(devlist_head); devlist_head = NULL; while(iter) { struct device_s *tmp = iter; iter = iter->next; free(tmp); } } /* Rebuild our internal scanner list */ for(cur_idx = 0; usbid[cur_idx].vendor_id; cur_idx++) sanei_usb_find_devices(usbid[cur_idx].vendor_id, usbid[cur_idx].product_id, attach); if(devlist) { for(i = 0; devlist[i]; i++) free(devlist[i]); free(devlist); } /* rebuild the sane-API scanner list array */ devlist = malloc(sizeof(devlist[0]) * (devlist_count + 1)); if(!devlist) return SANE_STATUS_NO_MEM; memset(devlist,0,sizeof(devlist[0]) * (devlist_count + 1)); for(i = 0, iter = devlist_head; i < devlist_count; i++, iter = iter->next) { devlist[i] = malloc(sizeof(SANE_Device)); if(!devlist[i]) { int j; for(j = 0; j < i; j++) free(devlist[j]); free(devlist); devlist = NULL; return SANE_STATUS_NO_MEM; } devlist[i]->name = iter->devname; devlist[i]->vendor = usbid[iter->idx].vendor_s; devlist[i]->model = usbid[iter->idx].model_s; devlist[i]->type = usbid[iter->idx].type_s; } if(device_list) *device_list = (const SANE_Device **) devlist ; return SANE_STATUS_GOOD; } SANE_Status sane_open(SANE_String_Const name, SANE_Handle *h) { struct device_s *dev = devlist_head; int ret; if(strlen(name)) for(; dev; dev = dev->next) if(!strcmp(name,dev->devname)) break; if(!dev) return SANE_STATUS_INVAL; /* Now open the usb device */ ret = sanei_usb_open(name,&(dev->dn)); if(ret != SANE_STATUS_GOOD) return ret; /* Claim the first interface */ ret = sanei_usb_claim_interface(dev->dn,0); if(ret != SANE_STATUS_GOOD) { sanei_usb_close(dev->dn); /* if we cannot claim the interface, this is because someone else is using it */ return SANE_STATUS_DEVICE_BUSY; } #ifdef HAVE_SANEI_USB_SET_TIMEOUT sanei_usb_set_timeout(30000); /* 30s timeout */ #endif *h = dev; return SANE_STATUS_GOOD; } void sane_close(SANE_Handle h) { struct device_s *dev = (struct device_s *) h; sanei_usb_release_interface(dev->dn,0); sanei_usb_close(dev->dn); } const SANE_Option_Descriptor * sane_get_option_descriptor (SANE_Handle h, SANE_Int option) { struct device_s *dev = (struct device_s *) h; if(option >= OPTION_MAX || option < 0) return NULL; return &(dev->optiond[option]); } static SANE_Status getvalue(SANE_Handle h, SANE_Int option, void * v) { struct device_s *dev = (struct device_s *) h; if(option != COLOR_OFFSET) *((SANE_Word *) v) = dev->optionw[option]; else { strcpy ((char *) v, dev->optiond[option].constraint.string_list[dev->optionw[option]]); } return SANE_STATUS_GOOD; } static const char * search_string_list(const SANE_String_Const * list, SANE_String value) { while (*list != NULL && strcmp(value, *list) != 0) { ++list; } return ((*list == NULL) ? NULL : (const char *) list); } static SANE_Status setvalue(SANE_Handle h, SANE_Int option, void * value, SANE_Int * info) { struct device_s *dev = (struct device_s *) h; SANE_Status status = SANE_STATUS_GOOD; int s_unit; if(option == 0) return SANE_STATUS_UNSUPPORTED; status = sanei_constrain_value (&(dev->optiond[option]), value, info); if (status != SANE_STATUS_GOOD) return status; if(info) *info |= SANE_INFO_RELOAD_PARAMS; switch(option) { case X1_OFFSET: case X2_OFFSET: /* X units */ /* convert into "scanner" unit, then back into mm */ dev->optionw[option] = *((SANE_Word *) value); s_unit = (int) ((dev->optionw[option] / ((double) MAX_X_S)) * MAX_X_H); dev->optionw[option] = (s_unit / ((double) MAX_X_H)) * MAX_X_S; if(info) *info |= SANE_INFO_INEXACT; break; case Y1_OFFSET: case Y2_OFFSET: /* Y units */ dev->optionw[option] = *((SANE_Word *) value); s_unit = (int) ((dev->optionw[option] / ((double) MAX_Y_S)) * MAX_Y_H); dev->optionw[option] = (s_unit / ((double) MAX_Y_H)) * MAX_Y_S; if(info) *info |= SANE_INFO_INEXACT; break; case COLOR_OFFSET: if(!strcmp((char *)value, mode_list[0])) dev->optionw[option] = GRAY; /* Gray */ else if(!strcmp((char *)value, mode_list[1])) dev->optionw[option] = RGB; /* RGB */ else return SANE_STATUS_INVAL; break; default: dev->optionw[option] = *((SANE_Word *) value); } return SANE_STATUS_GOOD; } SANE_Status sane_control_option(SANE_Handle h, SANE_Int option, SANE_Action a, void * v, SANE_Int * i) { if (option < 0 || option >= OPTION_MAX) return SANE_STATUS_INVAL; if(i) *i = 0; switch (a) { case SANE_ACTION_GET_VALUE: return getvalue(h,option,v); case SANE_ACTION_SET_VALUE: return setvalue(h,option,v,i); default: return SANE_STATUS_INVAL; } } SANE_Status sane_get_parameters(SANE_Handle h, SANE_Parameters *p) { struct device_s *dev = (struct device_s *) h; if(!p) return SANE_STATUS_INVAL; p->format = dev->optionw[COLOR_OFFSET] == RGB ? SANE_FRAME_RGB : SANE_FRAME_GRAY; p->last_frame = SANE_TRUE; p->depth = 8; update_img_size(dev); p->pixels_per_line = dev->width; p->lines = dev->height; p->bytes_per_line = p->pixels_per_line; if(p->format == SANE_FRAME_RGB) p->bytes_per_line *= 3; return SANE_STATUS_GOOD; } static void send_pkt(int command, int data_size, struct device_s *dev) { size_t size = 32; memset(dev->packet_data,0,size); dev->packet_data[0] = htonl(MAGIC_NUMBER); dev->packet_data[1] = htonl(command); dev->packet_data[5] = htonl(data_size); sanei_usb_write_bulk(dev->dn, (unsigned char *) dev->packet_data, &size); } /* s: printer status */ /* Return the next packet size */ static int wait_ack(struct device_s *dev, int *s) { SANE_Status ret; size_t size = 512; do { size = 512; ret = sanei_usb_read_bulk(dev->dn, (unsigned char *) dev->packet_data, &size); } while(SANE_STATUS_EOF == ret || size == 0); if(s) *s = ntohl(dev->packet_data[4]); return ntohl(dev->packet_data[5]); } static void send_conf(struct device_s *dev) { int y1,y2,x1,x2; size_t size = 100; y1 = (int) ((dev->optionw[Y1_OFFSET] / ((double) MAX_Y_S)) * MAX_Y_H); y2 = (int) ((dev->optionw[Y2_OFFSET] / ((double) MAX_Y_S)) * MAX_Y_H); x1 = (int) ((dev->optionw[X1_OFFSET] / ((double) MAX_X_S)) * MAX_X_H); x2 = (int) ((dev->optionw[X2_OFFSET] / ((double) MAX_X_S)) * MAX_X_H); dev->conf_data[0] = htonl(0x15); dev->conf_data[1] = htonl(dev->optionw[BRIGH_OFFSET]); dev->conf_data[2] = htonl(dev->optionw[CONTR_OFFSET]); dev->conf_data[3] = htonl(dev->optionw[RES_OFFSET]); dev->conf_data[4] = htonl(0x1); dev->conf_data[5] = htonl(0x1); dev->conf_data[6] = htonl(0x1); dev->conf_data[7] = htonl(0x1); dev->conf_data[8] = 0; dev->conf_data[9] = 0; dev->conf_data[10] = htonl(0x8); dev->conf_data[11] = 0; dev->conf_data[12] = 0; dev->conf_data[13] = 0; dev->conf_data[14] = 0; dev->conf_data[16] = htonl(y1); dev->conf_data[17] = htonl(x1); dev->conf_data[18] = htonl(y2); dev->conf_data[19] = htonl(x2); dev->conf_data[20] = 0; dev->conf_data[21] = 0; dev->conf_data[22] = htonl(0x491); dev->conf_data[23] = htonl(0x352); if(dev->optionw[COLOR_OFFSET] == RGB) { dev->conf_data[15] = htonl(0x2); dev->conf_data[24] = htonl(0x1); } else { dev->conf_data[15] = htonl(0x6); dev->conf_data[24] = htonl(0x0); } sanei_usb_write_bulk(dev->dn, (unsigned char *) dev->conf_data, &size); } static SANE_Status get_data(struct device_s *dev) { int color; size_t size = 512; int packet_size; unsigned char * buffer = (unsigned char *) dev->packet_data; if(dev->status != STATUS_SCANNING) return SANE_STATUS_IO_ERROR; /* first wait a standard data pkt */ do { size = 512; sanei_usb_read_bulk(dev->dn, buffer, &size); if(size) if(ntohl(dev->packet_data[0]) == MAGIC_NUMBER) { if(ntohl(dev->packet_data[1]) == PKT_DATA) break; if(ntohl(dev->packet_data[1]) == PKT_END_DATA) { dev->status = STATUS_IDLE; send_pkt(PKT_GO_IDLE,0,dev); wait_ack(dev,NULL); wait_ack(dev,NULL); send_pkt(PKT_UNKNOW_1,0,dev); wait_ack(dev,NULL); send_pkt(PKT_RESET,0,dev); return SANE_STATUS_EOF; } } } while(1); packet_size = ntohl(dev->packet_data[5]); if(!dev->buffer) { update_img_size(dev); dev->buffer = malloc(dev->bufs); if(!dev->buffer) return SANE_STATUS_NO_MEM; dev->write_offset_r = 0; dev->write_offset_g = 1; dev->write_offset_b = 2; } /* Get the "data header" */ do { size = 512; sanei_usb_read_bulk(dev->dn, buffer,&size); } while(!size); color = ntohl(dev->packet_data[0]); packet_size -= size; /* Now, read the data */ do { int j; int i; int ret; do { size = 512; ret = sanei_usb_read_bulk(dev->dn, buffer, &size); } while(!size || ret != SANE_STATUS_GOOD); packet_size -= size; switch(color) { case RED_LAYER: i = dev->write_offset_r + 3*size; if(i > dev->bufs) i = dev->bufs; for(j = 0; dev->write_offset_r < i; dev->write_offset_r += 3) dev->buffer[dev->write_offset_r] = buffer[j++]; break; case GREEN_LAYER: i = dev->write_offset_g + 3*size; if(i > dev->bufs) i = dev->bufs; for(j = 0; dev->write_offset_g < i; dev->write_offset_g += 3) dev->buffer[dev->write_offset_g] = buffer[j++]; break; case BLUE_LAYER: i = dev->write_offset_b + 3*size; if(i > dev->bufs) i = dev->bufs; for(j = 0; dev->write_offset_b < i; dev->write_offset_b += 3) dev->buffer[dev->write_offset_b] = buffer[j++]; break; case GRAY_LAYER: if(dev->write_offset_r + size >= dev->bufs) size = dev->bufs - dev->write_offset_r; memcpy(dev->buffer + dev->write_offset_r, buffer,size); dev->write_offset_r += size; break; } } while(packet_size > 0); return SANE_STATUS_GOOD; } SANE_Status sane_start(SANE_Handle h) { struct device_s *dev = (struct device_s *) h; int status; size_t size; dev->read_offset = 0; dev->write_offset_r = 0; dev->write_offset_g = 1; dev->write_offset_b = 2; dev->buffer = NULL; send_pkt(PKT_RESET,0,dev); send_pkt(PKT_READ_STATUS,0,dev); wait_ack(dev,&status); if(status) return SANE_STATUS_IO_ERROR; send_pkt(PKT_READCONF,0,dev); if(wait_ack(dev,NULL)) { size = 512; sanei_usb_read_bulk(dev->dn, (unsigned char *) dev->conf_data, &size); } send_pkt(PKT_SETCONF,100,dev); send_conf(dev); wait_ack(dev,NULL); send_pkt(PKT_START_SCAN,0,dev); wait_ack(dev,NULL); dev->status = STATUS_SCANNING; return SANE_STATUS_GOOD; } static int min3(int r, int g, int b) { /* Optimize me ! */ g--; b -= 2; if(r < g && r < b) return r; if(b < r && b < g) return b; return g; } SANE_Status sane_read(SANE_Handle h, SANE_Byte * buf, SANE_Int maxlen, SANE_Int * len) { struct device_s *dev = (struct device_s *) h; int available; int ret; *len = 0; if(dev->status == STATUS_IDLE) return SANE_STATUS_IO_ERROR; if(dev->optionw[COLOR_OFFSET] == RGB) { while(min3(dev->write_offset_r,dev->write_offset_g, dev->write_offset_b) <= dev->read_offset) { ret = get_data(dev); if(ret != SANE_STATUS_GOOD) { if(min3(dev->write_offset_r, dev->write_offset_g, dev->write_offset_b) <= dev->read_offset) return ret; } } available = min3(dev->write_offset_r,dev->write_offset_g, dev->write_offset_b); } else { while(dev->write_offset_r <= dev->read_offset) { ret = get_data(dev); if(ret != SANE_STATUS_GOOD) if(dev->write_offset_r <= dev->read_offset) return ret; } available = dev->write_offset_r; } *len = available - dev->read_offset; if(*len > maxlen) *len = maxlen; memcpy(buf,dev->buffer + dev->read_offset,*len); dev->read_offset += *len; return SANE_STATUS_GOOD; } void sane_cancel(SANE_Handle h) { struct device_s *dev = (struct device_s *) h; size_t size; SANE_Status ret; unsigned char * buffer = (unsigned char *) dev->packet_data; if(dev->buffer) { free(dev->buffer); dev->buffer = NULL; } if(dev->status != STATUS_SCANNING) return; do { size = 512; sanei_usb_read_bulk(dev->dn, buffer, &size); if(size) if(ntohl(dev->packet_data[0]) == MAGIC_NUMBER) { if(ntohl(dev->packet_data[1]) == PKT_END_DATA) { dev->status = STATUS_IDLE; send_pkt(PKT_GO_IDLE,0,dev); do { size = 512; ret = sanei_usb_read_bulk(dev->dn, buffer, &size); } while(size == 0 || ntohl(dev->packet_data[1]) != PKT_END_DATA); dev->status = STATUS_IDLE; send_pkt(PKT_GO_IDLE,0,dev); wait_ack(dev,NULL); wait_ack(dev,NULL); send_pkt(PKT_UNKNOW_1,0,dev); wait_ack(dev,NULL); send_pkt(PKT_RESET,0,dev); } } } while(1); } SANE_Status sane_set_io_mode(SANE_Handle __sane_unused__ handle, SANE_Bool __sane_unused__ non_blocking) { return SANE_STATUS_UNSUPPORTED; } SANE_Status sane_get_select_fd (SANE_Handle __sane_unused__ handle, SANE_Int __sane_unused__ * fd) { return SANE_STATUS_UNSUPPORTED; }