+u_int16_t gemtag_calc_crc(unsigned char *data,u_int16_t len) {
+
+ u_int16_t crc_polynom;
+ u_int16_t crc_preset;
+ u_int16_t crc;
+ int i,j;
+
+ crc_polynom=0x8408;
+ crc_preset=0xffff;
+ crc=0xffff;
+
+ for(i=0;i<len;i++) {
+ crc^=data[i];
+ for(j=0;j<8;j++) {
+ if(crc&0x0001)
+ crc=(crc>>1)^crc_polynom;
+ else
+ crc=(crc>>1);
+ }
+ }
+ return crc;
+}
+
+int gemtag_transceive(struct gemtag_handle *gh,unsigned char cmd,
+ unsigned char *tx,unsigned int tx_len,
+ unsigned char *rx,unsigned int *rx_len) {
+
+ unsigned char txbuf[256];
+ unsigned char rxbuf[256];
+ struct gemtag_cmd_hdr *txhdr;
+ struct gemtag_cmd_hdr *rxhdr;
+ u_int16_t crc,*crcptr;
+ int i,ret,size,rest;
+
+ txhdr=(struct gemtag_cmd_hdr *)txbuf;
+ rxhdr=(struct gemtag_cmd_hdr *)rxbuf;
+
+ txhdr->start=0xa5;
+ txhdr->seq=++(gh->seq);
+ txhdr->cmd=cmd;
+ txhdr->len=tx_len;
+ size=sizeof(struct gemtag_cmd_hdr);
+ memcpy(txbuf+size,tx,tx_len);
+ size+=tx_len;
+
+ /* crc check */
+ if(gh->caps&GEMTAG_CAP_CRC) {
+ crcptr=(u_int16_t *)(txbuf+size);
+ crc=gemtag_calc_crc(txbuf,size);
+ //*crcptr=(crc>>8)|(crc<<8);
+ *crcptr=crc;
+ size+=2;
+ }
+
+ /* usb write */
+ if(gh->caps&GEMTAG_CAP_VERB_TRANSMIT) {
+ printf("(%02d) -> ",size);
+ hexdump(txbuf,size);
+ }
+ ret=usb_interrupt_write(gh->handle,0x02,txbuf,size,0);
+ if(ret<=0) {
+ perror("usb interrupt write");
+ return ret;
+ }
+
+ /* usb read */
+ ret=usb_interrupt_read(gh->handle,0x81,rxbuf,32,0);
+ if(ret<=0) {
+ perror("usb interrupt read");
+ return ret;
+ }
+ if(ret<5) {
+ if(gh->caps&GEMTAG_CAP_VERB_TRANSMIT)
+ return -SHORT_ANSWER;
+ }
+
+ *rx_len=rxbuf[3]|(rxbuf[4]<<8);
+ size=*rx_len+sizeof(struct gemtag_cmd_hdr);
+ if(gh->caps&GEMTAG_CAP_CRC) size+=2;
+
+ i=1;
+ rest=size-ret;
+ while(rest>=0) {
+ ret=usb_interrupt_read(gh->handle,0x81,rxbuf+i*32,32,0);
+ if(ret<=0) {
+ perror("usb interrupt read (missing bytes)");
+ return ret;
+ }
+ i++;
+ rest-=ret;
+ }
+
+ if(gh->caps&GEMTAG_CAP_VERB_TRANSMIT) {
+ printf("(%02d) <- ",size);
+ hexdump(rxbuf,size);
+ }
+
+ /* crc check */
+ if(gh->caps&GEMTAG_CAP_CRC) {
+ size-=2;
+ crcptr=(u_int16_t *)(rxbuf+size);
+ crc=gemtag_calc_crc(rxbuf,size);
+ if(((crc>>8)!=rxbuf[size+1])||((crc&0xff)!=rxbuf[size]))
+ return -BAD_CRC;
+ }
+
+ /* check sequence number */
+ if(rxhdr->seq!=txhdr->seq) return -SEQ_MISMATCH;
+
+ /* check return code */
+ if(rxbuf[2]) return -CMD_FAILED;
+
+ memcpy(rx,rxbuf+sizeof(struct gemtag_cmd_hdr),*rx_len);
+
+ return 0;
+}
+