/* barcode.c -- sample program to talk to the Socket Communications "In-Hand Scan" CompactFlash-form-factor Barcode Scanner. Copyright 2001 Mark W. Eichin This code is public domain, but I would appreciate being creditted so that I can get feedback and enhancements from people. This code has been tested on an iPAQ under linux, and on an x86 laptop also under linux. Note that on most arm-linux releases, you need to use # setserial /dev/ttyS0 baud_base 460800 to force the baud_base to a value for which the baud rate settings actually work; hopefully this serial driver bug will be fixed at some point. Thanks to Jim Gettys (DEC/Compaq CRL) for loan of the iPAQ hardware, and Socket Communications for answering development-kit related questions. */ /* code based on mwe-sample.pl, from pdf version of 923 spec */ /* Serial code from my 1996 twconv.c for the twiddler */ #include #include /* for malloc */ #include /* for EINTR */ #include /* for write, sleep */ #include /* for timespec */ #include /* for ioctl, TIOC* */ #include /* for serial port open */ #include /* for serial port open */ #include /* for serial port open */ #include /* tcsetattr */ #include /* for sendmsg */ /* evil global variables */ int scanner_fileno = -1; /* dev macros */ #ifdef DEBUG #define D fprintf #else /* GNU extension! define an inline stdarg function if you port */ #define D(...) #endif /* Scanner-specific data */ /* names from 923guide.pdf(6-3) */ enum scan_msg { AIM_ON, AIM_OFF, BEEP, CMD_ACK, CMD_NAK, DECODE_DATA, EVENT, LED_OFF, LED_ON, PARAM_DEFAULTS, PARAM_REQUEST, PARAM_SEND, REPLY_REVISION, REQUEST_REVISION, SCAN_DISABLE, SCAN_ENABLE, SLEEP, START_DECODE, STOP_DECODE, WAKEUP, }; typedef enum scan_msg scan_msg; typedef struct scan_msg_entry { scan_msg verb; unsigned char *name; unsigned char opcode; enum argtype { NO_ARG, ONE_ARG, MANY_ARG, SPECIAL_WAKEUP } argtype; } scan_msg_entry; scan_msg_entry scanmsgs[] = { { AIM_ON, "AIM_ON", 0xc4, NO_ARG, }, { AIM_OFF, "AIM_OFF", 0xc5, NO_ARG, }, { BEEP, "BEEP", 0xe6, ONE_ARG, }, /* beep code */ { CMD_ACK, "CMD_ACK", 0xd0, NO_ARG, }, { CMD_NAK, "CMD_NAK", 0xd1, ONE_ARG, }, /* type of error */ { DECODE_DATA, "DECODE_DATA", 0xf3, MANY_ARG, }, { EVENT, "EVENT", 0xf6, ONE_ARG, }, /* event tag */ { LED_OFF, "LED_OFF", 0xe8, ONE_ARG, }, /* led # (always 1) */ { LED_ON, "LED_ON", 0xe7, ONE_ARG, }, /* led # (always 1) */ { PARAM_DEFAULTS, "PARAM_DEFAULTS", 0xc8, NO_ARG, }, { PARAM_REQUEST, "PARAM_REQUEST", 0xc7, MANY_ARG, }, { PARAM_SEND, "PARAM_SEND", 0xc6, MANY_ARG, }, { REPLY_REVISION, "REPLY_REVISION", 0xa4, MANY_ARG, }, { REQUEST_REVISION, "REQUEST_REVISION", 0xa3, NO_ARG, }, { SCAN_DISABLE, "SCAN_DISABLE", 0xea, NO_ARG, }, { SCAN_ENABLE, "SCAN_ENABLE", 0xe9, NO_ARG, }, { SLEEP, "SLEEP", 0xeb, NO_ARG, }, { START_DECODE, "START_DECODE", 0xe4, NO_ARG, }, { STOP_DECODE, "STOP_DECODE", 0xe5, NO_ARG, }, { WAKEUP, "WAKEUP", 0, SPECIAL_WAKEUP, }, /* just a NUL */ }; typedef enum token_tag { NAK_RESEND, NAK_BAD_CONTEXT, NAK_DENIED, DECODER_SE1200, DECODER_SE1200LR, DECODER_SE1200WA, DECODER_SE1200HV, DECODER_SE1200C1, DECODER_SE1200VHD, DECODER_SE900, DECODER_SE900C1, CODE_NA, CODE_39, CODE_93, CODE_128, CODE_CODABAR, CODE_DISCRETE_2_OF_5, CODE_IATA_2_of_5, CODE_INTERLEAVED_2_OF_5, CODE_MSI_PLESSEY, CODE_TRIOPTIC_39, CODE_BOOKLAND_EAN, CODE_COUPON, CODE_UPC_A, CODE_UPC_E0, CODE_UPC_E1, CODE_UPC_A_PLUS_2, CODE_UPC_E0_PLUS_2, CODE_UPC_E1_PLUS_2, CODE_UPC_A_PLUS_5, CODE_UPC_E0_PLUS_5, CODE_UPC_E1_PLUS_5, CODE_EAN_8, CODE_EAN_13, CODE_EAN_128, CODE_EAN_8_PLUS_2, CODE_EAN_13_PLUS_2, CODE_EAN_8_PLUS_5, CODE_EAN_13_PLUS_5, PARAM_BEEP_TONE, PARAM_LASER_TIME, PARAM_AIM_TIME, PARAM_POWER_MODE, PARAM_TRIGGER_MODE, PARAM_DUP_TIMEOUT, PARAM_BEEP_ON_GOOD, PARAM_XMIT_NR, PARAM_PERMIT_SCANNING, PARAM_LINEAR_SECURITY, PARAM_TWOWAY_REP, PARAM_UPC_A, PARAM_UPC_E, PARAM_UPC_E1, PARAM_EAN_8, PARAM_EAN_13, PARAM_BOOKLAND, PARAM_DECODE_PLUS, PARAM_DECODE_PLUS_REP, PARAM_UPC_A_CHECK, PARAM_UPC_E_CHECK, PARAM_UPC_E1_CHECK, PARAM_UPC_A_PRE, PARAM_UPC_E_PRE, PARAM_UPC_E1_PRE, PARAM_UPC_E_AS_A, PARAM_UPC_E1_AS_A, PARAM_EAN_8_ZERO, PARAM_EAN_8_AS_13, PARAM_UPC_SECURITY, PARAM_UPC_COUPON, PARAM_USS_128, PARAM_EAN_128, PARAM_ISBT_128, PARAM_CODE_39, PARAM_TRIOPTIC_39, PARAM_CODE_39_AS_32, PARAM_CODE_39_PRE, PARAM_CODE_39_LENGTH_1, PARAM_CODE_39_LENGTH_2, PARAM_CODE_39_VERIFY, PARAM_CODE_39_CHECK, PARAM_CODE_39_ASCII, PARAM_CODE_93, PARAM_CODE_93_LENGTH_1, PARAM_CODE_93_LENGTH_2, PARAM_INTERLEAVED_2_OF_5, PARAM_INTERLEAVED_2_OF_5_LENGTH_1, PARAM_INTERLEAVED_2_OF_5_LENGTH_2, PARAM_INTERLEAVED_2_OF_5_VERIFY, PARAM_INTERLEAVED_2_OF_5_CHECK, PARAM_INTERLEAVED_2_OF_5_AS_EAN_13, PARAM_DISCRETE_2_OF_5, PARAM_DISCRETE_2_OF_5_LENGTH_1, PARAM_DISCRETE_2_OF_5_LENGTH_2, PARAM_CODABAR, PARAM_CODABAR_LENGTH_1, PARAM_CODABAR_LENGTH_2, PARAM_CLSI_EDIT, PARAM_NOTIS_EDIT, PARAM_MSI_PLESSEY, PARAM_MSI_PLESSEY_LENGTH_1, PARAM_MSI_PLESSEY_LENGTH_2, PARAM_MSI_PLESSEY_VERIFY, PARAM_MSI_PLESSEY_CHECK, PARAM_MSI_PLESSEY_ALGO, PARAM_XMIT_CODE_ID, PARAM_DECODE_PREFIX, PARAM_DECODE_SUFFIX_1, PARAM_DECODE_SUFFIX_2, PARAM_DECODE_PACKET, PARAM_SCAN_DATA_FORMAT, PARAM_BAUD, PARAM_PARITY, PARAM_STOP_BITS, PARAM_HANDSHAKE, PARAM_HOST_RESPONSE_TIMEOUT, PARAM_INTERCHAR_DELAY, PARAM_HOST_CHAR_TIMEOUT, PARAM_EVENT_PREFIX, PARAM_EVENT_DECODE, PARAM_EVENT_BOOTUP, PARAM_EVENT_PARAM, PARAM_SCAN_ANGLE, PARAM_ALL, } token_tag; typedef enum token_class { NAK, DECODER, CODE, PARAM, PARAM1, } token_class; typedef struct token_name_entry { token_class tcl; token_tag tag; unsigned char *name; unsigned char value; } token_name_entry; token_name_entry token_names[] = { { NAK, NAK_RESEND, "RESEND (bad cksum)", 1 }, { NAK, NAK_BAD_CONTEXT, "BAD_CONTEXT (bug?)", 2 }, { NAK, NAK_DENIED, "DENIED", 6 }, { DECODER, DECODER_SE1200, "SE 1200 Standard", 0x00 }, { DECODER, DECODER_SE1200LR, "SE 1200LR (Long Range)", 0x01 }, { DECODER, DECODER_SE1200WA, "SE 1200WA (Wide Angle)", 0x02 }, { DECODER, DECODER_SE1200HV, "SE 1200HV (High Visibility)", 0x03 }, { DECODER, DECODER_SE1200C1, "SE 1200C1 (Class 1)", 0x04 }, { DECODER, DECODER_SE1200VHD, "SE 1200VHD (Very High Density)", 0x05 }, { DECODER, DECODER_SE900, "SE 900 Standard", 0x28 }, { DECODER, DECODER_SE900C1, "SE 900C1 IEC Class 1", 0x2a }, /* 6-16, table 6-6, "Supported Code Types" */ { CODE, CODE_NA, "Not Applicable", 0x00 }, { CODE, CODE_39, "Code 39", 0x01 }, { CODE, CODE_CODABAR, "Codabar", 0x02 }, { CODE, CODE_128, "Code 128", 0x03 }, { CODE, CODE_DISCRETE_2_OF_5, "Discrete 2 of 5", 0x04 }, { CODE, CODE_IATA_2_of_5, "IATA 2 of 5", 0x05 }, { CODE, CODE_INTERLEAVED_2_OF_5, "Interleaved 2 of 5", 0x06 }, { CODE, CODE_93, "Code 93", 0x07 }, { CODE, CODE_UPC_A, "UPC A", 0x08 }, { CODE, CODE_UPC_E0, "UPC E0", 0x09 }, { CODE, CODE_EAN_8, "EAN 8", 0x0A }, { CODE, CODE_EAN_13, "EAN 13", 0x0B }, { CODE, CODE_MSI_PLESSEY, "MSI Plessey", 0x0E }, { CODE, CODE_EAN_128, "EAN 128", 0x0F }, { CODE, CODE_UPC_E1, "UPC E1", 0x10 }, { CODE, CODE_TRIOPTIC_39, "Trioptic Code 39", 0x15 }, { CODE, CODE_BOOKLAND_EAN, "Bookland EAN", 0x16 }, { CODE, CODE_COUPON, "Coupon Code", 0x17 }, { CODE, CODE_EAN_8_PLUS_2, "EAN 8 with 2 Supps.", 0x4A }, { CODE, CODE_EAN_13_PLUS_2, "EAN 13 with 2 Supps.", 0x4B }, { CODE, CODE_UPC_A_PLUS_2, "UPC A with 2 Supps.", 0x48 }, { CODE, CODE_UPC_E0_PLUS_2, "UPC E0 with 2 Supps.", 0x49 }, { CODE, CODE_UPC_E1_PLUS_2, "UPC E1 with 2 Supps.", 0x50 }, { CODE, CODE_UPC_A_PLUS_5, "UPC A with 5 Supps.", 0x88 }, { CODE, CODE_UPC_E0_PLUS_5, "UPC E0 with 5 Supps.", 0x89 }, { CODE, CODE_EAN_8_PLUS_5, "EAN 8 with 5 Supps.", 0x8A }, { CODE, CODE_EAN_13_PLUS_5, "EAN 13 with 5 Supps.", 0x8B }, { CODE, CODE_UPC_E1_PLUS_5, "UPC E1 with 5 Supps.", 0x90 }, /* 5-2, table 5-1, parameters */ { PARAM, PARAM_BEEP_TONE, "Beeper Tone", 0x91 }, /* Medium Frequency 5-9 */ { PARAM, PARAM_LASER_TIME, "Laser On Time", 0x88 }, /* 3.0 sec 5-10 */ { PARAM, PARAM_AIM_TIME, "Aim Duration", 0xED }, /* 0.0 sec 5-11 */ { PARAM, PARAM_POWER_MODE, "Power Mode", 0x80 }, /* Low Power 5-12 */ { PARAM, PARAM_TRIGGER_MODE, "Trigger Mode", 0x8A }, /* Level 5-13 */ { PARAM, PARAM_DUP_TIMEOUT, "Time-out Between Same Symbol", 0x89 }, /* 1.0 sec 5-15 */ { PARAM, PARAM_BEEP_ON_GOOD, "Beep After Good Decode", 0x38 }, /* Enable 5-16 */ { PARAM, PARAM_XMIT_NR, "Transmit No Read Message", 0x5E }, /* Disable 5-17 */ { PARAM, PARAM_PERMIT_SCANNING, "Parameter Scanning", 0xEC }, /* Enable 5-18 */ { PARAM, PARAM_LINEAR_SECURITY, "Linear Code Type Security Levels", 0x4E }, /* 1 5-19 */ { PARAM, PARAM_TWOWAY_REP, "Bi-directional Redundancy", 0x43 }, /* Disable 5-22 */ /* UPC/EAN */ { PARAM, PARAM_UPC_A, "UPC-A", 0x01 }, /* Enable 5-23 */ { PARAM, PARAM_UPC_E, "UPC-E", 0x02 }, /* Enable 5-24 */ { PARAM, PARAM_UPC_E1, "UPC-E1", 0x0C }, /* Disable 5-25 */ { PARAM, PARAM_EAN_8, "EAN-8", 0x04 }, /* Enable 5-26 */ { PARAM, PARAM_EAN_13, "EAN-13", 0x03 }, /* Enable 5-27 */ { PARAM, PARAM_BOOKLAND, "Bookland EAN", 0x53 }, /* Disable 5-28 */ { PARAM, PARAM_DECODE_PLUS, "Decode UPC/EAN Supplementals", 0x10 }, /* Ignore 5-30 */ { PARAM, PARAM_DECODE_PLUS_REP, "Decode UPC/EAN Supplemental Redundancy", 0x50 }, /* 7 5-31 */ { PARAM, PARAM_UPC_A_CHECK, "Transmit UPC-A Check Digit", 0x28 }, /* Enable 5-32 */ { PARAM, PARAM_UPC_E_CHECK, "Transmit UPC-E Check Digit", 0x29 }, /* Enable 5-33 */ { PARAM, PARAM_UPC_E1_CHECK, "Transmit UPC-E1 Check Digit", 0x2A }, /* Enable 5-34 */ { PARAM, PARAM_UPC_A_PRE, "UPC-A Preamble", 0x22 }, /* System Character 5-35 */ { PARAM, PARAM_UPC_E_PRE, "UPC-E Preamble", 0x23 }, /* System Character 5-36 */ { PARAM, PARAM_UPC_E1_PRE, "UPC-E1 Preamble", 0x24 }, /* System Character 5-37 */ { PARAM, PARAM_UPC_E_AS_A, "Convert UPC-E to A", 0x25 }, /* Disable 5-38 */ { PARAM, PARAM_UPC_E1_AS_A, "Convert UPC-E1 to A", 0x26 }, /* Disable 5-39 */ { PARAM, PARAM_EAN_8_ZERO, "EAN-8 Zero Extend", 0x27 }, /* Disable 5-40 */ { PARAM, PARAM_EAN_8_AS_13, "Convert EAN-8 to EAN-13 Type", 0xE0 }, /* Type is EAN-13 5-41 */ { PARAM, PARAM_UPC_SECURITY, "UPC/EAN Security Level", 0x4D }, /* 0 5-42 */ { PARAM, PARAM_UPC_COUPON, "UPC/EAN Coupon Code", 0x55 }, /* Disable 5-44 */ /* Code 128 */ { PARAM, PARAM_USS_128, "USS-128", 0x08 }, /* Enable 5-45 */ { PARAM, PARAM_EAN_128, "UCC/EAN-128", 0x0E }, /* Enable 5-46 */ { PARAM, PARAM_ISBT_128, "ISBT 128", 0x54 }, /* Enable 5-47 */ { PARAM, PARAM_CODE_39, "Code 39", 0x00 }, /* Enable 5-48 */ { PARAM, PARAM_TRIOPTIC_39, "Trioptic Code 39", 0x0D }, /* Disable 5-49 */ { PARAM, PARAM_CODE_39_AS_32, "Convert Code 39 to Code 32", 0x56 }, /* Disable 5-50 */ { PARAM, PARAM_CODE_39_PRE, "Code 32 Prefix", 0xE7 }, /* Disable 5-51 */ { PARAM, PARAM_CODE_39_LENGTH_1, "Set Length 1 for Code 39", 0x12 }, /* 2..55, 5-53 */ { PARAM, PARAM_CODE_39_LENGTH_2, "Set Length 2 for Code 39", 0x13 }, /* 2..55, 5-53 */ { PARAM, PARAM_CODE_39_VERIFY, "Code 39 Check Digit Verification", 0x30 }, /* Disable 5-54 */ { PARAM, PARAM_CODE_39_CHECK, "Transmit Code 39 Check Digit", 0x2B }, /* Disable 5-55 */ { PARAM, PARAM_CODE_39_ASCII, "Code 39 Full ASCII Conversion", 0x11 }, /* Disable 5-56 */ /* Code 93 */ { PARAM, PARAM_CODE_93, "Code 93", 0x09 }, /* Disable 5-57 */ { PARAM, PARAM_CODE_93_LENGTH_1, "Set Length 1 for Code 93", 0x1A }, /* 4..55, 5-58 */ { PARAM, PARAM_CODE_93_LENGTH_2, "Set Length 2 for Code 93", 0x1B }, /* 4..55, 5-58 */ /* Interleaved 2 of 5 */ { PARAM, PARAM_INTERLEAVED_2_OF_5, "Interleaved 2 of 5", 0x06 }, /* Enable 5-60 */ { PARAM, PARAM_INTERLEAVED_2_OF_5_LENGTH_1, "Set Length 1 for Interleaved 2 of 5", 0x16 }, /* 14 5-61 */ { PARAM, PARAM_INTERLEAVED_2_OF_5_LENGTH_2, "Set Length 2 for Interleaved 2 of 5", 0x17 }, /* 14 5-61 */ { PARAM, PARAM_INTERLEAVED_2_OF_5_VERIFY, "Interleaved 2 of 5 Check Digit Verification", 0x31 }, /* Disable 5-63 */ { PARAM, PARAM_INTERLEAVED_2_OF_5_CHECK, "Transmit Interleaved 2 of 5 Check Digit", 0x2C }, /* Disable 5-64 */ { PARAM, PARAM_INTERLEAVED_2_OF_5_AS_EAN_13, "Convert Interleaved 2 of 5 to EAN 13", 0x52 }, /* Disable 5-65 */ /* Discrete 2 of 5 */ { PARAM, PARAM_DISCRETE_2_OF_5, "Discrete 2 of 5", 0x05 }, /* Disable 5-66 */ { PARAM, PARAM_DISCRETE_2_OF_5_LENGTH_1, "Set Length 1 for Discrete 2 of 5", 0x14 }, /* 12 5-67 */ { PARAM, PARAM_DISCRETE_2_OF_5_LENGTH_2, "Set Length 2 for Discrete 2 of 5", 0x15 }, /* 12 5-67 */ /* Codabar */ { PARAM, PARAM_CODABAR, "Codabar", 0x07 }, /* Disable 5-69 */ { PARAM, PARAM_CODABAR_LENGTH_1, "Set Length 1 for Codabar", 0x18 }, /* 5-55 */ { PARAM, PARAM_CODABAR_LENGTH_2, "Set Length 2 for Codabar", 0x19 }, /* 5-71 */ { PARAM, PARAM_CLSI_EDIT, "CLSI Editing", 0x36 }, /* Disable 5-72 */ { PARAM, PARAM_NOTIS_EDIT, "NOTIS Editing", 0x37 }, /* Disable 5-73 */ /* MSI Plessey */ { PARAM, PARAM_MSI_PLESSEY, "MSI Plessey", 0x0B }, /* Disable 5-74 */ { PARAM, PARAM_MSI_PLESSEY_LENGTH_1, "Set Length 1 for MSI Plessey", 0x1E }, /* 6..55 5-76 */ { PARAM, PARAM_MSI_PLESSEY_LENGTH_2, "Set Length 2 for MSI Plessey", 0x1F }, /* 6..55 5-76 */ { PARAM, PARAM_MSI_PLESSEY_VERIFY, "MSI Plessey Check Digits", 0x32 }, /* One 5-77 */ { PARAM, PARAM_MSI_PLESSEY_CHECK, "Transmit MSI Plessey Check Digit", 0x2E }, /* Disable 5-78 */ { PARAM, PARAM_MSI_PLESSEY_ALGO, "MSI Plessey Check Digit Algorithm", 0x33 }, /* Mod 10/Mod 10 5-79 */ /* Data Options */ { PARAM, PARAM_XMIT_CODE_ID, "Transmit Code ID Character", 0x2D }, /* None 5-81 */ /* Prefix/Suffix Values */ { PARAM, PARAM_DECODE_PREFIX, "Prefix", 0x69 }, /* NULL 5-82 */ { PARAM, PARAM_DECODE_SUFFIX_1, "Suffix 1", 0x68 }, /* LF 5-82 */ { PARAM, PARAM_DECODE_SUFFIX_2, "Suffix 2", 0x6A }, /* CR 5-82 */ { PARAM, PARAM_SCAN_DATA_FORMAT, "Scan Data Transmission Format", 0xEB }, /* Data as is 5-84 */ /* Serial Interface */ { PARAM, PARAM_BAUD, "Baud Rate", 0x9C }, /* 9600 5-88 */ { PARAM, PARAM_PARITY, "Parity", 0x9E }, /* None 5-89 */ { PARAM, PARAM_HANDSHAKE, "Software Handshaking", 0x9F }, /* Enable 5-91 */ { PARAM, PARAM_DECODE_PACKET, "Decode Data Packet Format", 0xEE }, /* Unpacketed 5-92 */ { PARAM, PARAM_HOST_RESPONSE_TIMEOUT, "Host Serial Response Time-out (in tenths of a second)", 0x9B }, /* 2 sec 5-93 */ { PARAM, PARAM_STOP_BITS, "Stop Bit Select", 0x9D }, /* 1 5-94 */ { PARAM, PARAM_INTERCHAR_DELAY, "Intercharacter Delay", 0x6E }, /* 0 5-95 */ { PARAM, PARAM_HOST_CHAR_TIMEOUT, "Host Character Time-out (in 10msec units)", 0xEF }, /* 200 msec 5-96 */ /* Event Reporting */ /* note that these are two byte, we just cheat */ { PARAM, PARAM_EVENT_PREFIX, "Event Prefix ", 0xF0 }, { PARAM1, PARAM_EVENT_DECODE, "Decode Event", 0x00 }, /* Disable 5-98 */ { PARAM1, PARAM_EVENT_BOOTUP, "Boot Up Event", 0x02 }, /* Disable 5-99 */ { PARAM1, PARAM_EVENT_PARAM, "Parameter Event", 0x03 }, /* Disable 5-100 */ /* Scan Angle */ { PARAM, PARAM_SCAN_ANGLE, "Scan Angle", 0xBF }, /* Normal Width 5-101 */ { PARAM, PARAM_ALL, "Request ALL Values", 0xFE }, }; unsigned char *lookup_opcode_name(unsigned char opcode) { int i; for (i=0; i< sizeof(scanmsgs)/sizeof(*scanmsgs); i++) { if(scanmsgs[i].opcode == opcode) { return scanmsgs[i].name; } } return 0; } unsigned char lookup_scan_opcode(scan_msg verb) { int i; for (i=0; i< sizeof(scanmsgs)/sizeof(*scanmsgs); i++) { if(scanmsgs[i].verb == verb) { return scanmsgs[i].opcode; } } return 0; } unsigned char *lookup_token_value(token_class tcl, unsigned char value) { int i; for (i=0; i< sizeof(token_names)/sizeof(*token_names); i++) { if(token_names[i].tcl == tcl && token_names[i].value == value) { return token_names[i].name; } } return 0; } unsigned char lookup_tag_value(token_tag tag) { int i; for (i=0; i< sizeof(token_names)/sizeof(*token_names); i++) { if(token_names[i].tag == tag) { return token_names[i].value; } } return 0; } token_tag lookup_value_tag(token_class tcl, unsigned char value) { int i; for (i=0; i< sizeof(token_names)/sizeof(*token_names); i++) { if(token_names[i].tcl == tcl && token_names[i].value == value) { return token_names[i].tag; } } return 0; } void shortsleep(double s) { struct timespec ts; ts.tv_sec = (int)s; ts.tv_nsec = (s-(double)(ts.tv_sec))*1e9; while(nanosleep(&ts, &ts) < 0) { /* didn't complete? */ if (errno == EINTR) continue; /* ok if interrupt */ break; /* maybe not ok */ } } void raise_rts_not() { int gmi = 0; int st; st = ioctl(scanner_fileno,TIOCMGET, &gmi); if(st) perror("tiocmget"); gmi |= TIOCM_RTS; st = ioctl(scanner_fileno,TIOCMSET, &gmi); if(st) perror("tiocmset"); D(stderr, "[raised rtsnot]"); return; } void drop_rts_not() { int gmi = 0; int st; st = ioctl(scanner_fileno,TIOCMGET, &gmi); if(st) perror("tiocmget"); gmi &= ~TIOCM_RTS; st = ioctl(scanner_fileno,TIOCMSET, &gmi); if(st) perror("tiocmset"); D(stderr, "[dropped rtsnot]"); return; } void wait_raise_cts_not() { int gmi = 0; int st; int cnt = 0; for (;cnt < 10000000;cnt++) { st = ioctl(scanner_fileno,TIOCMGET, &gmi); if(st) perror("tiocmget"); if(gmi & TIOCM_CTS) { D(stderr, "[ctsnot up (%d)]", cnt); return; } } D(stderr, "[ctsnot up timeout (%d)]\n", cnt); return; } void wait_drop_cts_not() { int gmi = 0; int st; int cnt = 0; for (;cnt < 10000000;cnt++) { st = ioctl(scanner_fileno,TIOCMGET, &gmi); if(st) perror("tiocmget"); if(!(gmi & TIOCM_CTS)) { D(stderr, "[ctsnot down (%d)]", cnt); return; } } D(stderr, "[ctsnot down timeout (%d)]\n", cnt); return; } void emit_char(unsigned char c) { /* shortsleep(0.001); */ write(scanner_fileno,&c,sizeof c); /* just goes to stdout for now */ } void fetch_char(unsigned char *c) { int st; for(;;) { st = read(scanner_fileno, c, sizeof(*c)); if (st == sizeof(*c)) return; if (st < 0) { perror("read"); exit(2); } /* relies on VTIME setting on scanner_fileno */ D(stderr,"null read?\n"); drop_rts_not(); wait_drop_cts_not(); shortsleep(0.1); raise_rts_not(); wait_raise_cts_not(); } } void send_ack(void); void pkt_read() { unsigned char len; unsigned char opcode; unsigned char source; unsigned char status; unsigned short cksum_accum = 0; unsigned char *dat = 0; int i = 0; /* shortsleep(0.01); */ D(stderr, "starting pkt_read\n"); fetch_char(&len); cksum_accum -= len; len--; /* len counts! */ fetch_char(&opcode); cksum_accum -= opcode; len--; D(stderr, "got %d bytes, opcode %02x\n", len+2, opcode); { unsigned char *msg= lookup_opcode_name(opcode); if(msg)fprintf(stderr,"Opcode=%s\n",msg); } fetch_char(&source); cksum_accum -= source; len--; D(stderr, "source %02x\n", source); if(source != 0) fprintf(stderr, "source(%02x) != decoder(0)\n", source); fetch_char(&status); cksum_accum -= status; len--; D(stderr, "status %02x\n", status); dat = malloc(len+1); while(len > 0) { /* fetch_char handles timeout-and-bounce-status */ fetch_char(&dat[i]); cksum_accum -= dat[i]; i++; len--; D(stderr, "%02x.(%d)", dat[i-1], len); fprintf(stderr, "<"); } dat[i] = 0; D(stderr, "got %d\n",len); /* opcode specific processing */ if (opcode == lookup_scan_opcode(CMD_NAK)) { unsigned char *msg=lookup_token_value(NAK, dat[0]); if(msg)fprintf(stderr,"NAK type=%s\n",msg); } else if (opcode == lookup_scan_opcode(REPLY_REVISION)) { int j; unsigned char *msg=0; fprintf(stderr, "REPLY_REVISION: Software rev["); for (j = 0; j", dat[j], msg); else fprintf(stderr, " unknown/reserved type %02x", dat[j]); j++; fprintf(stderr, " Decoder Checksum %02x%02x\n", dat[j], dat[j+1]); } else if (opcode == lookup_scan_opcode(DECODE_DATA)) { unsigned char *codetype = lookup_token_value(CODE, dat[0]); if (codetype) fprintf(stderr, "DECODE_DATA: type %s, scan [%s]\n", codetype, dat+1); else fprintf(stderr, "DECODE_DATA with INVALID type\n"); } else if (opcode == lookup_scan_opcode(PARAM_SEND)) { /* probably response to PARAM_REQUEST */ /* just loop through... */ int j; unsigned char* msg; fprintf(stderr, "PARAM_SEND from device (beep code 0x%x):\n", dat[0]); fprintf(stderr, "Value\t\tName\n"); fprintf(stderr, "-------\t\t-------------------\n"); for (j = 1; j < i; j++) { /* d[i] == trailing 0 */ switch(lookup_value_tag(PARAM, dat[j])) { case PARAM_EVENT_PREFIX: msg = lookup_token_value(PARAM1, dat[++j]); if (!msg) msg="UNDOCUMENTED EXTENDED FIELD"; break; default: msg = lookup_token_value(PARAM, dat[j]); break; } if (!msg) msg="UNDOCUMENTED FIELD"; j++; fprintf(stderr, "0x%x(%d)\t%s\n", dat[j], dat[j], msg); } } /* add more decoders here */ { unsigned short cksum; unsigned char dat; fetch_char(&dat); /* D(stderr, "{cksum hi %02x}", dat); */ cksum = dat << 8; fetch_char(&dat); /* D(stderr, "{cksum lo %02x}", dat); */ cksum |= dat; D(stderr, "[cksum %04x/accum %04x]\n", cksum, cksum_accum); if (cksum != cksum_accum) fprintf(stderr, "Checksum mismatch! [cksum %04x/accum %04x]\n", cksum, cksum_accum); } shortsleep(0.01); /* now ack things */ if (opcode == lookup_scan_opcode(CMD_ACK)) { } else if (opcode == lookup_scan_opcode(CMD_NAK)) { } else { D(stderr, "Sending ACK for 0x%02x\n", opcode); send_ack(); } } void wakeup(void) { unsigned char wakestr[] = { 0 /*, 0, 0 */ }; write(scanner_fileno,&wakestr,sizeof wakestr); } void spew(size_t s_len, unsigned char* s) { size_t i; for (i = 0; i"); emit_char(s[i]); /* shortsleep(0.2); */ } D(stderr, "\n"); } int global_rexmit = 0; void spew_makemsg(unsigned char opcode, unsigned char *s, size_t s_len) { unsigned char hdr[] = { 0, /* opcode */ 4, /* source = host */ 0 }; /* status */ unsigned char* msg = malloc(sizeof(hdr) + s_len + 1 /* len */ + 2 /* cksum */); int i; unsigned short cksum = 0; /* uint_16_t... */ if (global_rexmit) { hdr[2] = 1; global_rexmit = 0; } hdr[0] = opcode; msg[0] = 1 + s_len + sizeof(hdr); memcpy(msg+1, hdr, sizeof(hdr)); memcpy(msg+1+sizeof(hdr), s, s_len); for (i = 0; i < 1+sizeof(hdr)+s_len; i++) { cksum += msg[i]; } cksum = 65536 - cksum; msg[1+sizeof(hdr)+s_len] = cksum >> 8; msg[1+sizeof(hdr)+s_len+1] = cksum & 255; drop_rts_not(); wait_drop_cts_not(); shortsleep(0.1); spew(1+sizeof(hdr)+s_len+2, msg); shortsleep(0.1); raise_rts_not(); wait_raise_cts_not(); free(msg); } void send_ack() { D(stderr, "Sending ACK\n"); wait_raise_cts_not(); /* should be true */ { unsigned char s[] = { }; spew_makemsg(0xd0, s, sizeof(s)); /* CMD_ACK */ } } void sendmsg(scan_msg verb, ...) { va_list ap; int i; scan_msg_entry *thismsg = 0; unsigned short cksum; unsigned char *freeme = 0; unsigned char *workmsg = 0; unsigned char basemsg[] = { 0, /* length */ 0, /* opcode */ 4, /* source = host */ 0, /* status */ 0, /* optional 1-byte content */ 0, 0, }; /* cksum */ va_start(ap, verb); workmsg = basemsg; for (i=0; i< sizeof(scanmsgs)/sizeof(*scanmsgs); i++) { if(scanmsgs[i].verb == verb) { thismsg = &scanmsgs[i]; break; } } if (!thismsg) return; /* error */ workmsg[1] = thismsg->opcode; switch (thismsg->argtype) { case NO_ARG: workmsg[0] = 4; break; case ONE_ARG: workmsg[0] = 5; workmsg[4] = va_arg(ap, int); break; case MANY_ARG: /* -1 term */ { int len = 4; /* starting size+offset */ int bytearg; freeme = malloc(len); /* error check! */ memcpy(freeme, workmsg, len); while( (bytearg = va_arg(ap, int)) != -1) { freeme = realloc(freeme, len+1); freeme[len++] = bytearg; } freeme[0] = len; freeme = realloc(freeme, len+2); /* room for cksum */ workmsg = freeme; } break; default: abort(); break; } cksum = 0; for (i = 0; i < workmsg[0]; i++) { cksum += workmsg[i]; } cksum = 65536 - cksum; workmsg[workmsg[0]] = cksum >> 8; workmsg[workmsg[0]+1] = cksum & 255; /* brag about it */ fprintf(stderr, "Sending: %s (%d bytes)\n", thismsg->name, workmsg[0]); /* actually send it */ drop_rts_not(); wait_drop_cts_not(); shortsleep(0.1); spew(workmsg[0]+2, workmsg); shortsleep(0.1); raise_rts_not(); wait_raise_cts_not(); if (freeme) free(freeme); } void usage(char *argv0) { fprintf(stderr, "Usage: %s [--diag] [--port dev] [--help]\n", argv0); fprintf(stderr, "\t--diag\tRun more debugging queries like PARAM_REQUEST(ALL)\n"); fprintf(stderr, "\t--port dev\tSpecify the serial port to which the scanner is attached\n"); } int main(int argc, char**argv) { int i; int diag_mode = 0; char *scanner_port_name = "/dev/ttyS2"; /* parse some args */ for (i = 1; i < argc; i++) { if (!strcmp(argv[i], "--diag")) { diag_mode = 1; continue; } else if (!strcmp(argv[i], "--port")) { i++; if (i < argc) { scanner_port_name = argv[i]; continue; } } else if (!strcmp(argv[i], "--help")) { usage(argv[0]); exit(0); } usage(argv[0]); exit(1); } /* system("stty raw -iexten -onlcr -echo -echoe -echok -echoctl -echoke 9600 < /dev/ttyS2"); */ scanner_fileno = open(scanner_port_name, O_NOCTTY|O_RDWR|O_SYNC); { int st; struct termios tio; st = tcgetattr(scanner_fileno, &tio); if (st < 0) { perror("tcgetattr"); exit(1); } tio.c_cflag = 0; tio.c_lflag = 0; cfmakeraw(&tio); cfsetospeed(&tio, B9600); cfsetispeed(&tio, B9600); tio.c_oflag &= (~ONLCR); tio.c_cflag |= (CLOCAL|CREAD|HUPCL); D(stderr, "iflag 0x%x oflag 0x%x cflag 0x%x lflag 0x%x\n", tio.c_iflag, tio.c_oflag, tio.c_cflag, tio.c_lflag); memset(&tio.c_cc, 0, sizeof(tio.c_cc)); tio.c_cc[VMIN] = 0; tio.c_cc[VTIME] = 2; /* PARAM_HOST_RESPONSE_TIMEOUT */ st = tcsetattr(scanner_fileno, TCSANOW, &tio); if (st < 0) { perror("tcsetattr"); exit(1); } } if (scanner_fileno < 0) { perror("scanner open"); exit(2); } fprintf(stderr, "============================\n"); /* must turn on the LED */ sendmsg(LED_ON, 1); pkt_read(); /* param_request(ALL) */ #define L(x) lookup_tag_value(x) #define END (-1) if(diag_mode) { sendmsg(PARAM_REQUEST, L(PARAM_ALL), END); pkt_read(); /* param_send(beep: 0x0f, boot-event(f0 02)=1 */ /* sendmsg(PARAM_SEND, 0xff, 0xf0, 0x02, 0, 0xf0, 0x00, 0, -1); */ sendmsg(PARAM_SEND, 0xff, L(PARAM_EVENT_PREFIX), L(PARAM_EVENT_BOOTUP), 0, L(PARAM_EVENT_PREFIX), L(PARAM_EVENT_DECODE), 0, END); pkt_read(); /* param_request(boot-event, decode-event) */ sendmsg(PARAM_REQUEST, L(PARAM_EVENT_PREFIX), L(PARAM_EVENT_BOOTUP), L(PARAM_EVENT_PREFIX), L(PARAM_EVENT_DECODE), END); pkt_read(); sendmsg(LED_ON, 1); pkt_read(); sendmsg(REQUEST_REVISION); pkt_read(); sendmsg(BEEP, 0x0e); pkt_read(); /* 1 byte params */ if(0) for (i=0; i< 0xf0; ) { sendmsg(PARAM_REQUEST, i+0, i+1, i+2, i+3, i+4, i+5, i+6, i+7, END); pkt_read(); i+=8; } /* 2 byte params */ sendmsg(PARAM_REQUEST, L(PARAM_EVENT_PREFIX), L(PARAM_EVENT_BOOTUP), L(PARAM_EVENT_PREFIX), L(PARAM_EVENT_DECODE), L(PARAM_EVENT_PREFIX), L(PARAM_EVENT_PARAM), END); pkt_read(); /* param_send(beep: 0x16, trigger: host */ sendmsg(PARAM_SEND, 0x16, L(PARAM_TRIGGER_MODE), 8, END); pkt_read(); /* param_send(beep: 0x17, laser on max 9.9s */ sendmsg(PARAM_SEND, 0xff, L(PARAM_LASER_TIME), 99, END); pkt_read(); /* param_send(beep: none, xmit NR */ sendmsg(PARAM_SEND, 0xff, L(PARAM_XMIT_NR), 1, END); pkt_read(); /* param_send(send packeted decode data) */ sendmsg(PARAM_SEND, 0xff, L(PARAM_DECODE_PACKET), 1, END); pkt_read(); /* param_send(beep on good) */ sendmsg(PARAM_SEND, 0xff, L(PARAM_BEEP_ON_GOOD), 1, END); pkt_read(); /* param_send(beep freq high) */ sendmsg(PARAM_SEND, 0xff, L(PARAM_BEEP_TONE), 0, END); pkt_read(); /* param_request(packet mode, trigger mode, NR, beep-on-good, beep freq) */ sendmsg(PARAM_REQUEST, L(PARAM_DECODE_PACKET), L(PARAM_TRIGGER_MODE), L(PARAM_XMIT_NR), L(PARAM_BEEP_ON_GOOD), L(PARAM_BEEP_TONE), END); pkt_read(); } else { /* just set up the scan */ /* param_send(beep: none, trigger: host */ sendmsg(PARAM_SEND, 0xff, L(PARAM_TRIGGER_MODE), 8, /* let the host fire it */ L(PARAM_XMIT_NR), 1, /* transmit NR for no scan */ L(PARAM_DECODE_PACKET), 1, /* send packet-form decoded data */ END); pkt_read(); } sendmsg(SCAN_ENABLE); pkt_read(); sendmsg(START_DECODE); pkt_read(); /* stall for a decode */ pkt_read(); exit(0); }