[sane-devel] Canon N670U:merlin670-V0.01 developers release (small bugfix)

kilgota@banach.math.auburn.edu kilgota@banach.math.auburn.edu
Thu, 31 Jan 2002 20:43:17 -0600 (CST)


As mentioned already, this program does not seem to want to create a
temporary file called n670u.raw . As mentioned below, one can always
create it first, with "touch," but introduction of a small change on line
593 of the file will take care of it, too.

There, it is possible to add to the "open" operation the option O_CREAT
and this takes care of the problem. Suitably edited file attached at the
end of this note.

Theodore Kilgore


---------- Forwarded message ----------
Date: Thu, 31 Jan 2002 19:40:45 -0600 (CST)
From: kilgota@khayyam.math.auburn.edu
Reply-To: kilgota@banach.math.auburn.edu
To: Gunther.Mayer@t-online.de
Cc: sane-devel@mostang.com
Subject: Re: [sane-devel] Canon N670U:merlin670-V0.01 developers release

This works. I got a scan out of it.

Further remarks:

1. apparently I had to rmmod scanner first, or it was unhappy.
2. I had to run the program as root
3. It didn't want somehow to create the temporary file n670u.raw, but
after I did

touch n670u.raw

it went right ahead and did the scan and exited after going the length of
an A4 paper.

4. Congratulations. Not bad.


Theodore Kilgore



> ---------- Forwarded message ----------
> Date: Thu, 31 Jan 2002 23:44:56 +0100
> From: Gunther Mayer <Gunther.Mayer@t-online.de>
> To: Sane-devel@mostang.com
> Subject: [sane-devel] Canon N670U:merlin670-V0.01 developers release
>
> Hi,
> my hack "merlin670" already scans at 150 dpi color:
> http://home.t-online.de/home/gunther.mayer/merlin-V0.01.c
>
> This is an early release for developers, no fancy features!
> You must even twiddle to get it compiled. See comments in the source.
>
> This is
> - based on libusb (no kernel module scanner.o needed).
> - standalone wrt SANE
>
> Regards, Gunther
> _______________________________________________
> Sane-devel mailing list
> Sane-devel@www.mostang.com
> http://www.mostang.com/mailman/listinfo/sane-devel
>

----------------edited merlin-V0.01.c follows------------------------

/*
 * Canon N670U Scanner LM9833 merlin test quick'n dirty
   (c) Gunther Mayer 2002
   GNU Public license
 *
 *  Test program
 *  For developers only !!!
 *  Just get it scanning, pending refinements.

 INSTALLING and USING:
 1) get,compile,install libusb-0.1.4
    No need for "scanner.o" kernel module !
 2) mkdir merlin in libusb's directory (so ".." works)
 3) cp merlin670.c merlin
 4) cc -o merlin670 -lusb -lm merlin670.c
    adjust libraries as needed
 5) ./merlin670 will do a 150 dpi color scan
    When you press Ctrl-C "n670u.ppm" will be written (else ca. A4 size
    will be scanned)
  */

/* CANON USB models (Canon Vendor ID VID=0x04A9):
  PID  model
  ???  FB320U
  ???  FB1210U
  ???  FB630U/636U
  ???  D2400UF
  ???  D660U
  ???  D1230U
  2202 FB620U
  2206 N650U
  2207 N1220U
  220B D646U
  220D N670U/N676U  ***
  220E N1240U

  N-Series: CIS Scanner
  D-Series: CCD Scanner
*/

#include <stdio.h>
#include "../usb.h" //libusb !
#include "../usbi.h"
#include <errno.h>
#include <sys/types.h>
#include <fcntl.h>
#include <math.h>
#include <signal.h>

#define wr(reg,val) writereg(reg,val)
#define wr2(reg,val) {writereg(reg+1,val&0xff); writereg(reg,val>>8);}
void init_like_3a(int);
usb_dev_handle *u;
usb_dev_handle *findscanner(int vendor, int product);
int fraw,ctrlc;

void raw2ppm(int lines,int pixperline)
{  int f,j=0,i=0,rr,gg,bb;
   FILE *o;
   char r[16384],g[16384],b[16384];

   printf("Creating n670u.ppm %dx%d\n",pixperline,lines);
   f=open("n670u.raw", O_RDONLY);
   o=fopen("n670u.ppm","w");
   fprintf(o,"P6\n%d %d\n255\n",pixperline,lines);

   while(j<lines) {
    j++;
    rr=read(f,r,pixperline);
    gg=read(f,g,pixperline);
    bb=read(f,b,pixperline);
    for(i=0;i<pixperline;i++)
     fprintf(o,"%c%c%c",r[i],g[i],b[i]);
   }
   fclose(o);
}

int writereg(int reg, int val)
{ char buf[5]; int i;

  buf[0]=0;  //0=write command
  buf[1]=reg;//  register
  buf[2]=0;  // MSB number of data bytes to write
  buf[3]=1;  // LSB number of data bytes to write
  buf[4]=val;
  //int usb_bulk_write(usb_dev_handle *dev, int ep, char *bytes, int size, int timeout);
  i=usb_bulk_write(u,3, buf, 5, 500 /*msec*/);
  if(i!=5) {
    printf(" write reg 0x%02x:0x%02x usb_bulk_write returned %d\n",reg,val,i);
    exit(1);
  }
}

int readreg_n(int reg,int n, char *b)
{ char buf[5];int i;

  buf[0]=1;     //1=read command
  buf[1]=reg;   // register
  buf[2]=n>>8;  // MSB of data bytes to read
  buf[3]=n&0xff;// LSB of data bytes to read

  i=usb_bulk_write(u,3, buf, 4, 500 /*msec*/);
  if(i!=4) {
    printf("usb_bulk_write returned %d\n",i);
    exit(1);
  }
  i=usb_bulk_read(u,2, b, n, 500);
  if(i!=n) {
    printf("usb_bulk_read returned %d\n",i);
    exit(1);
  }
  return i;
}

unsigned char readreg(int reg)
{ char buf[5];
  char reply[1]; int i;

  buf[0]=1;  //1=read command
  buf[1]=reg;// register
  buf[2]=0;  // MSB number
  buf[3]=1;  // LSB number
  i=usb_bulk_write(u,3, buf, 4, 500 /*msec*/);
  if(i!=4) {
    printf("usb_bulk_write returned %d\n",i);
    exit(1);
  }
  i=usb_bulk_read(u,2, reply, 1, 500);
  if(i!=1)
    printf("usb_bulk_read returned %d\n",i);
  return reply[0];
}

// read registers from Scanner:
void dumpregs()
{ int i;

  for(i=0x0;i<0x80;i++) {
    if((i%16) ==0) printf("\n0x%02x:",i);
    if((i%8)==0) printf(" ");
    if(i==0 || i==5 || i==6)  // the dataport read returns with "0 Bytes read", of course.
      printf("XX ");
    else
      printf("%02x ",readreg(i));
       }
  printf("\n");
}

// datasheet chapter 10.2
void do_softreset()
{
 printf("softreset\n");
 writereg(0x18,0x05);
 writereg(0x07,0);    // idle state
 writereg(0x18,0x18); // disable sampling
 writereg(0x07,0x20); // trigger reset
 writereg(0x18,0x05);

 writereg(0x07,0);    //idle
}

void buttons()
{ int i=10;

  printf("reg7=0x%02x , reg2 is 0x%02x\n",readreg(7),readreg(2));
  writereg(0x59,0);
  printf("reg7=0x%02x , reg2 is 0x%02x\n",readreg(7),readreg(2));
  writereg(0x5a,0x90); // Defunct beim Schreiben von 0x10 !!!
  printf("reg7=0x%02x , reg2 is 0x%02x\n",readreg(7),readreg(2));
  //writereg(0x5b,0);

  while(i) { // i--;
    printf("reg2 is 0x%02x\n",readreg(2));
  }
}

void scan150_ini(void)
{

  wr(0x08,0x06); //mclk
  //wr(0x08,0x0a);
  wr(0x09,0x1c); //150dpi

  wr(0x22,0); //pix start
  wr(0x23,0x4b);
  wr(0x24,0x13); // A4
  //wr(0x25,0xab);
  wr(0x25,0xae);  // LineDataSize is rounded up to the next even integer 1242 !?
  //wr(0x24,0xb2); // nogo

  wr(0x29,0x02); //lamps
  wr2(0x2c,0x0017);
  wr2(0x2e,0x11d2);
  wr2(0x30,0x0017);
  wr2(0x32,0x10db);
  wr2(0x34,0x0017);
  wr2(0x36,0x0c04);

  wr(0x38,1);
  wr(0x39,1);
  wr(0x3a,1);
  wr(0x3b,1);
  wr(0x3c,1);
  wr(0x3d,1);

  //wr(0x42,0x20); //nono 0x26 notjet
  wr(0x42,0x21);
  wr(0x45,0x13); // motor on

  wr2(0x46,0x01f2); //stepsize
  wr2(0x48,0x01f2); // FF stepize

  wr(0x4e,0x2f);
  wr(0x4f,0x01);

  wr(0x50,0x3f);
  wr(0x51,0xfc);  // phase difference on pause
  wr(0x52,0x77);  //0x07 from snoop would produce colorstripes at pause
  wr(0x53,0xc8);

  wr(0x58,0); // Sensors must be off, else scanning just halts.
}

// write 4096 bytes to the data port (register 0x06) per color.
// The LM9833 will use the 12 most significant bits of
// 16bit ADC as index. So we get a 8 Bit output value per color
// LM9833 pdf Figure 43 is plain wrong (as it shows 12 bit values
//   to be written to the dataport)
void download_gamma(float gamma)  // Gamma=1 for Linear.
{
 char buf[0x14];
 int color,i,j,r; double x,y;

 for(color=0;color<3;color++) {
 wr(0x03,0x02 + color*4); // Bits 0 and 1:0x02 Bits 2 and 3:color
 wr(0x04,0x00);
 wr(0x05,0x00);

 for(i=0;i<=0xff;i++) {
  buf[0]=0;  //0=write command
  buf[1]=0x06;  /*  register*/
  buf[2]=0;// MSB number
  buf[3]=0x10;  // LSB number bytes written
  for(j=0x4;j<0x14;j++) {
    x=(i*16 + j-4)/4096.0;
    y=pow(x,1.0/gamma)*256 ;
    buf[j]=y;
    //printf("x gam is %f %f %f\n",x,y,gamma);
  }
  r=usb_bulk_write(u,3, buf, 0x14, 500 /*msec*/);
  if(r!=0x14) {
    printf(" write reg 0x%02x: gamma usb_bulk_write returned %d\n",6,r);
    exit(1);
  }
 }
 }
}

void mysigint(int s)
{ printf("CTRL-C received.\n");
  ctrlc=1;
}

void scan150(void)
{ int z=0,i=0,k=0;
  int r,r1,r3,r7,r4c,r4d; char buf[0x2000];
  int lastr1,lastr3,lastr7;

  signal(SIGINT,mysigint);

  scan150_ini(); //geht jetzt!
  //scan150_ini_from_register_dump(); // doesnt work

  printf("Register Dump before scan:");
  dumpregs();

  /*wr(0x03,0x0a);
  wr(0x04,0x40);
  wr(0x05,0x00);*/
  r=readreg(0x06);
  printf("reg6 is 0x%02x\n",r);

  printf("registercheck before scan:");
  { int r1,r3,r7,r4c,r4d; char buf[0x2000];
    r1=readreg(0x01);
    r3=readreg(0x03);
    r7=readreg(0x07);
    r4c=readreg(0x4c);
    r4d=readreg(0x4d);
    printf("r1=%d r3=0x%02x r7=0x%2x r4c4d=%02x%02x\n",r1,r3,r7,r4c,r4d);
  }

  printf("download gamma 2.5...\n");
  download_gamma(2.5);

  //wr(0x4a,5); //skip
  /*wr(0x58,0); // sensor home OFF
  wr(0x07,0x00);*/

  printf("\nSCAN 150dpi COLOR until CTRL-C or DIN A4\n");
  wr(0x07,0x03); // scannnnn
  while(1) {
    r1=readreg(0x01);
    r3=readreg(0x03);
    r7=readreg(0x07);
    if(ctrlc==1) break;
    //if((lastr1!=r1)||(lastr3!=r3)||(lastr7!=r7))
    //  printf("r1=%d r3=0x%02x r7=0x%2x\n",r1,r3,r7);
    if(r1>0x02) {
      r=readreg_n(0x00,0x2000,buf);
      k=k+0x2000;
      z=k/1242/3-1;
      if(z>1650) break; // ca. DINA4
      write(fraw,buf,0x2000);
      close(fraw);
      fraw=open("n670u.raw", O_RDWR|O_APPEND);
      //printf("READ=%d ",r);
      printf("."); fflush(stdout);
    }
    lastr3=r3; lastr1=r1; lastr7=r7;
  }
  printf("\n");
  do_softreset();
  raw2ppm(z,1242);
  init_like_3a(1);
}

void playlamp()
{
  wr(0x08,0x3f); // warning: psychedelic frequencies
  wr2(0x2c,0x0017);
  wr2(0x2e,0x1225);
  wr2(0x30,0x0017);
  wr2(0x32,0x1225);
  wr2(0x34,0x0017);
  wr2(0x36,0x1225);
  wr(0x29,0x02);
  dumpregs();
}

void init_like_3a(int early)
{ int r,i;

  wr(0x03,0);
  wr(0x04,0);
  wr(0x05,0);
  wr(0x06,0);
  wr(0x08,0x0a);
  wr(0x09,0x1a);
  wr(0x0a,0);
  wr(0x0b,0x15);
  wr(0x0c,0x4c);
  wr(0x0d,0x2f);
  wr(0x0e,0);
  wr(0x0f,0);
  wr(0x10,0);
  wr(0x11,0x04);
  wr(0x12,0x05);
  wr(0x13,0x06);
  wr(0x14,0x07);
  wr(0x15,0);
  wr(0x16,0);
  wr(0x17,0);
  wr(0x18,0x05);
  wr(0x19,0);
  wr(0x1a,0);
  wr(0x1b,0x01);
  wr(0x1c,0);
  wr(0x1d,0);
  wr(0x1e,0);
  wr(0x1f,0x4b);
  wr(0x20,0x14);
  wr(0x21,0xad);
  wr(0x22,0);
  wr(0x23,0x4b);
  wr(0x24,0x14);
  wr(0x25,0x3b);
  wr(0x26,0x15);
  wr(0x27,0);
  // 0x28 reserved
  wr(0x29,0);
  wr(0x2a,0);
  wr(0x2b,0);
  wr(0x2c,0x03);
  wr(0x2d,0xff);
  wr(0x2e,0);
  wr(0x2f,0x01);
  wr(0x30,0x03);
  wr(0x31,0xff);
  wr(0x32,0);
  wr(0x33,0x01);
  wr(0x34,0x03);
  wr(0x35,0xff);
  wr(0x36,0);
  wr(0x37,0x01);
  wr(0x38,0);
  wr(0x39,0);
  wr(0x3a,0);
  wr(0x3b,0x01);
  wr(0x3c,0x01);
  wr(0x3d,0);
  wr(0x3e,0);
  wr(0x3f,0);
  wr(0x40,0x40);
  wr(0x41,0);
  wr(0x42,0x20);
  wr(0x43,0);
  wr(0x44,0);
  wr(0x45,0x13);
  wr(0x46,0x03);
  wr(0x47,0xe3);
  wr(0x48,0x01);
  wr(0x49,0xf2);
  wr(0x4a,0);
  wr(0x4b,0);
  wr(0x4c,0);
  wr(0x4d,0);
  wr(0x4e,0x8c);
  wr(0x4f,0x01);
  wr(0x50,0);
  wr(0x51,0xfc);
  wr(0x52,0x35);
  wr(0x53,0x94);
  wr(0x54,0);
  wr(0x55,0x0f);
  wr(0x56,0x08);
  wr(0x57,0x1f);
  wr(0x58,0x04);
  wr(0x59,0x44);
  // 5a 5b
  wr(0x5c,0x01);
  wr(0x5d,0);
  wr(0x5e,0);
  wr(0x5f,0);
  wr(0x60,0);
  wr(0x61,0);
  wr(0x62,0);
  wr(0x63,0);
  wr(0x64,0);
  wr(0x65,0);
  wr(0x66,0);
  wr(0x67,0);
  wr(0x68,0);
  wr(0x69,0);
  wr(0x6a,0);
  wr(0x6b,0);
  wr(0x6c,0);
  wr(0x6d,0);
  wr(0x6e,0);
  wr(0x6f,0);
  wr(0x70,0);
  wr(0x71,0);
  wr(0x72,0);
  wr(0x73,0);
  wr(0x74,0);
  wr(0x75,0);
  wr(0x76,0);
  wr(0x77,0);
  wr(0x78,0);
  wr(0x79,0);
  wr(0x7a,0);
  wr(0x7b,0);
  wr(0x7c,0);
  wr(0x7d,0);
  wr(0x7e,0);
  wr(0x7f,0);
  // 5a 5b
  wr(0x5a,0x94);
  wr(0x5b,0x19);

/*
  wr(0x07,0); // URB 127
  { int r18;
    r18=readreg(0x18);
    wr(0x18,0x18);
    wr(0x07,0x20);
    wr(0x18,r18); //0x05
    wr(0x07,0x00);
    wr(0x07,0x00);
  }
*/

  wr(0x5b,0x11); // URB 140
  wr(0x5b,0x91);
  r=readreg(0x5a);
  wr(0x5a,0x14);
  r=readreg(0x09);
  wr(0x09,r);

  wr(0x5a,0x16); //URB 151    ERR-1
  wr(0x59,0x66); // URB 152

  wr(0x70,0x73); // URB 406

  wr(0x2c,0x3f); //URB 473 LAMP off
  wr(0x30,0x3f);
  wr(0x34,0x3f);

  //playlamp(); exit(1);

  //wr(0x48,0x01); // fast feed step size
  wr(0x48,0x00);
  wr(0x49,0xf2);
  wr(0x57,0x3f); // //URB 421 pwm
  //wr(0x57,0x1f);

  //wr(0x58,0);// XXXXXXXXXX

  r=readreg(0x02);
  printf("r2 is 0x%02x\n",r);
  if(r==0) {printf("r is null.exit\n");exit(1);}

  if(!(r&1)) {// not at home position
     printf("Homing sensor...\n");
     wr(0x07,02);
     while(1) {
      static int lastr=2000; int r7,r2;
      r2=readreg(0x02);
      r7=readreg(0x07);
      if(lastr!=r7)     printf("r2=0x%02x, r7=%02x\n",r2,r7);
      lastr=r7;
      if(r7!=2) break;
     }
  }

  if(early) return;

  wr(0x4a,0x2);
  printf("Sensor prog FORW\n");
  wr(0x07,0x05);
  while(1) {
      static int lastr=2000; int r2,r7;
      r2=readreg(0x02);
      r7=readreg(0x07);
      if(lastr!=r7)     printf("r2=0x%02x, r7=%02x\n",r2,r7);
      lastr=r7;
      if(r7!=5) break;
  }

  wr(0x07,0x02); // REVERSE until HOME
  i=10;
  while(1) {
    int r2,r7; static int lastr=2000;
    r2=readreg(0x02);
    r7=readreg(0x07);
    if(lastr!=r)     printf("r2=0x%02x, r7=%02x\n",r2,r7);
    lastr=r;
    if(r7!=2) break;
  }
  return;

  wr(0x57,0x33); //URB 570

  wr(0x07,0x01); //FORWARD 2581ms
  wr(0x51,0x00); // accel profile

  // wait for reg02 go from 63->62
  while(1) {
    int r;
    r=readreg(0x02);
    printf("r2 is 0x%02x  ",r);
    if(r!=0x63) break;
  }
  wr(0x07,0x00); // STOP
}


int main(void)
{ int i=0,r=0;

  printf("Merlin670 V0.01 for developers only\n");
  u=findscanner(0x04a9 /*canon*/ ,0x220d /* N670U/N676U*/);

  if(u==NULL) {printf("scanner not found. exiting.\n"); return 1;}

  //printf("found u=%p\n",u);
  //printf("Bus/Device= %p/%p, config=%d interface=%d altsetting=%d\n",
  //	u->bus, u->device, u->config, u->interface, u->altsetting);

  i=usb_claim_interface(u,0);
  printf("usb_claim_interface returned %d\n",i);
  if(i!=0) { printf("could not claim scanner)! exiting.\n");
    exit(1);}

  i=usb_set_configuration(u,1);
   printf("usb_set_configuration returned %d\n",i);
  if(i!=0) exit(1);

  writereg(7,0);
  printf("Initial Register Dump",i,r);
  dumpregs();

  fraw=open("n670u.raw",O_CREAT|O_TRUNC|O_RDWR);
  if(fraw==-1)
    {printf("couldn open n670u.raw, errno=%d\n",errno); exit(1);}

  init_like_3a(0);
  scan150();

  //buttons(); exit(1);
  return 0;
}


usb_dev_handle *findscanner(int vendor, int product)
{
  struct usb_bus *bus;
  struct usb_device *dev;

  usb_init();
  usb_find_busses();
  usb_find_devices();

  printf("Searching USB for CanoScan N670U/N676U  VID=%04x PID=%04x\n",vendor,product);
  printf("bus/device  idVendor/idProduct\n");
  for (bus = usb_busses; bus; bus = bus->next) {
    for (dev = bus->devices; dev; dev = dev->next) {
      printf("%s/%s     %04X/%04X\n", bus->dirname, dev->filename,
	dev->descriptor.idVendor, dev->descriptor.idProduct);
	if((dev->descriptor.idVendor==vendor)&&
	   (dev->descriptor.idProduct==product))
           return usb_open(dev);
      }
  }

  return NULL;
}