1141cc406Sopenharmony_ci/**
2141cc406Sopenharmony_ci * Description of the Primax PagePartner model
3141cc406Sopenharmony_ci */
4141cc406Sopenharmony_cistatic P5_Model pagepartner_model = {
5141cc406Sopenharmony_ci  "Primax PagePartner",
6141cc406Sopenharmony_ci  "Primax",
7141cc406Sopenharmony_ci  "PagePartner",
8141cc406Sopenharmony_ci  SANE_I18N ("sheetfed scanner"),
9141cc406Sopenharmony_ci
10141cc406Sopenharmony_ci  {300, 200, 150, 100, 0},
11141cc406Sopenharmony_ci  /* 500 seems also possible */
12141cc406Sopenharmony_ci  {600, 400, 300, 200, 150, 100, 0},
13141cc406Sopenharmony_ci
14141cc406Sopenharmony_ci  300,
15141cc406Sopenharmony_ci  600,
16141cc406Sopenharmony_ci  100,
17141cc406Sopenharmony_ci  100,
18141cc406Sopenharmony_ci  16,
19141cc406Sopenharmony_ci
20141cc406Sopenharmony_ci  SANE_FIX (0.0),
21141cc406Sopenharmony_ci  SANE_FIX (0.0),
22141cc406Sopenharmony_ci  SANE_FIX (215.9),
23141cc406Sopenharmony_ci  SANE_FIX (300.0),
24141cc406Sopenharmony_ci};
25141cc406Sopenharmony_ci
26141cc406Sopenharmony_ci#ifdef HAVE_LINUX_PPDEV_H
27141cc406Sopenharmony_cistatic char *
28141cc406Sopenharmony_ciaddr_name (uint16_t addr)
29141cc406Sopenharmony_ci{
30141cc406Sopenharmony_ci  switch (addr)
31141cc406Sopenharmony_ci    {
32141cc406Sopenharmony_ci    case DATA:
33141cc406Sopenharmony_ci      return "DATA";
34141cc406Sopenharmony_ci      break;
35141cc406Sopenharmony_ci    case STATUS:
36141cc406Sopenharmony_ci      return "STATUS";
37141cc406Sopenharmony_ci      break;
38141cc406Sopenharmony_ci    case CONTROL:
39141cc406Sopenharmony_ci      return "CONTROL";
40141cc406Sopenharmony_ci      break;
41141cc406Sopenharmony_ci    case EPPADR:
42141cc406Sopenharmony_ci      return "EPPADR";
43141cc406Sopenharmony_ci      break;
44141cc406Sopenharmony_ci    case EPPDATA:
45141cc406Sopenharmony_ci      return "EPPDATA";
46141cc406Sopenharmony_ci      break;
47141cc406Sopenharmony_ci    default:
48141cc406Sopenharmony_ci      return "*ERROR*";
49141cc406Sopenharmony_ci    }
50141cc406Sopenharmony_ci}
51141cc406Sopenharmony_ci#endif
52141cc406Sopenharmony_ci
53141cc406Sopenharmony_ci/** @brief low level hardware access functions
54141cc406Sopenharmony_ci * @{
55141cc406Sopenharmony_ci */
56141cc406Sopenharmony_ci
57141cc406Sopenharmony_cistatic uint8_t
58141cc406Sopenharmony_cip5_inb (int fd, uint16_t addr)
59141cc406Sopenharmony_ci{
60141cc406Sopenharmony_ci#ifdef HAVE_LINUX_PPDEV_H
61141cc406Sopenharmony_ci  uint8_t val = 0xff;
62141cc406Sopenharmony_ci  int rc, mode = 0xff;
63141cc406Sopenharmony_ci
64141cc406Sopenharmony_ci  switch (addr)
65141cc406Sopenharmony_ci    {
66141cc406Sopenharmony_ci    case DATA:
67141cc406Sopenharmony_ci      rc = ioctl (fd, PPRDATA, &val);
68141cc406Sopenharmony_ci      break;
69141cc406Sopenharmony_ci    case STATUS:
70141cc406Sopenharmony_ci      rc = ioctl (fd, PPRSTATUS, &val);
71141cc406Sopenharmony_ci      break;
72141cc406Sopenharmony_ci    case CONTROL:
73141cc406Sopenharmony_ci      rc = ioctl (fd, PPRCONTROL, &val);
74141cc406Sopenharmony_ci      break;
75141cc406Sopenharmony_ci    case EPPDATA:
76141cc406Sopenharmony_ci      mode = 1;			/* data_reverse */
77141cc406Sopenharmony_ci      rc = ioctl (fd, PPDATADIR, &mode);
78141cc406Sopenharmony_ci      mode = IEEE1284_MODE_EPP | IEEE1284_DATA;
79141cc406Sopenharmony_ci      rc = ioctl (fd, PPSETMODE, &mode);
80141cc406Sopenharmony_ci#ifdef PPSETFLAGS
81141cc406Sopenharmony_ci      mode = PP_FASTREAD;
82141cc406Sopenharmony_ci      rc = ioctl (fd, PPSETFLAGS, &mode);
83141cc406Sopenharmony_ci#endif
84141cc406Sopenharmony_ci      rc = read (fd, &val, 1);
85141cc406Sopenharmony_ci      break;
86141cc406Sopenharmony_ci    default:
87141cc406Sopenharmony_ci      DBG (DBG_error, "p5_inb(%s) escaped ppdev\n", addr_name (addr));
88141cc406Sopenharmony_ci      return 0xFF;
89141cc406Sopenharmony_ci    }
90141cc406Sopenharmony_ci  if (rc < 0)
91141cc406Sopenharmony_ci    {
92141cc406Sopenharmony_ci      DBG (DBG_error, "ppdev ioctl returned <%s>\n", strerror (errno));
93141cc406Sopenharmony_ci    }
94141cc406Sopenharmony_ci  return val;
95141cc406Sopenharmony_ci#else
96141cc406Sopenharmony_ci  if(fd && addr)
97141cc406Sopenharmony_ci    return 0;
98141cc406Sopenharmony_ci  return 0;
99141cc406Sopenharmony_ci#endif
100141cc406Sopenharmony_ci}
101141cc406Sopenharmony_ci
102141cc406Sopenharmony_cistatic void
103141cc406Sopenharmony_cip5_outb (int fd, uint16_t addr, uint8_t value)
104141cc406Sopenharmony_ci{
105141cc406Sopenharmony_ci#ifdef HAVE_LINUX_PPDEV_H
106141cc406Sopenharmony_ci  int rc = 0, mode = 0xff;
107141cc406Sopenharmony_ci
108141cc406Sopenharmony_ci  switch (addr)
109141cc406Sopenharmony_ci    {
110141cc406Sopenharmony_ci    case DATA:
111141cc406Sopenharmony_ci      rc = ioctl (fd, PPWDATA, &value);
112141cc406Sopenharmony_ci      break;
113141cc406Sopenharmony_ci    case CONTROL:
114141cc406Sopenharmony_ci      mode = value & 0x20;
115141cc406Sopenharmony_ci      rc = ioctl (fd, PPDATADIR, &mode);
116141cc406Sopenharmony_ci      if (!rc)
117141cc406Sopenharmony_ci	{
118141cc406Sopenharmony_ci	  value = value & 0xDF;
119141cc406Sopenharmony_ci	  rc = ioctl (fd, PPWCONTROL, &value);
120141cc406Sopenharmony_ci	}
121141cc406Sopenharmony_ci      break;
122141cc406Sopenharmony_ci    case EPPDATA:
123141cc406Sopenharmony_ci      mode = 0;			/* data forward */
124141cc406Sopenharmony_ci      rc = ioctl (fd, PPDATADIR, &mode);
125141cc406Sopenharmony_ci      mode = IEEE1284_MODE_EPP | IEEE1284_DATA;
126141cc406Sopenharmony_ci      rc = ioctl (fd, PPSETMODE, &mode);
127141cc406Sopenharmony_ci      rc = write (fd, &value, 1);
128141cc406Sopenharmony_ci      break;
129141cc406Sopenharmony_ci    case EPPADR:
130141cc406Sopenharmony_ci      mode = 0;			/* data forward */
131141cc406Sopenharmony_ci      rc = ioctl (fd, PPDATADIR, &mode);
132141cc406Sopenharmony_ci      mode = IEEE1284_MODE_EPP | IEEE1284_ADDR;
133141cc406Sopenharmony_ci      rc = ioctl (fd, PPSETMODE, &mode);
134141cc406Sopenharmony_ci      rc = write (fd, &value, 1);
135141cc406Sopenharmony_ci      break;
136141cc406Sopenharmony_ci    default:
137141cc406Sopenharmony_ci      DBG (DBG_error, "p5_outb(%s,0x%02x) escaped ppdev\n", addr_name (addr),
138141cc406Sopenharmony_ci	   value);
139141cc406Sopenharmony_ci      break;
140141cc406Sopenharmony_ci    }
141141cc406Sopenharmony_ci  if (rc < 0)
142141cc406Sopenharmony_ci    {
143141cc406Sopenharmony_ci      DBG (DBG_error, "ppdev ioctl returned <%s>\n", strerror (errno));
144141cc406Sopenharmony_ci    }
145141cc406Sopenharmony_ci#else
146141cc406Sopenharmony_ci  if(fd && addr && value)
147141cc406Sopenharmony_ci    return;
148141cc406Sopenharmony_ci#endif /* HAVE_LINUX_PPDEV_H */
149141cc406Sopenharmony_ci}
150141cc406Sopenharmony_ci
151141cc406Sopenharmony_cistatic void
152141cc406Sopenharmony_ciwrite_reg (int fd, uint8_t index, uint8_t value)
153141cc406Sopenharmony_ci{
154141cc406Sopenharmony_ci  uint8_t idx;
155141cc406Sopenharmony_ci
156141cc406Sopenharmony_ci  /* both nibbles hold the same value */
157141cc406Sopenharmony_ci  idx = index & 0x0F;
158141cc406Sopenharmony_ci  DBG (DBG_io2, "write_reg(REG%X,0x%x)\n", idx, value);
159141cc406Sopenharmony_ci  idx = idx << 4 | idx;
160141cc406Sopenharmony_ci  p5_outb (fd, EPPADR, idx);
161141cc406Sopenharmony_ci  p5_outb (fd, EPPDATA, value);
162141cc406Sopenharmony_ci}
163141cc406Sopenharmony_ci
164141cc406Sopenharmony_cistatic uint8_t
165141cc406Sopenharmony_ciread_reg (int fd, uint8_t index)
166141cc406Sopenharmony_ci{
167141cc406Sopenharmony_ci  uint8_t idx;
168141cc406Sopenharmony_ci
169141cc406Sopenharmony_ci  /* both nibbles hold the same value */
170141cc406Sopenharmony_ci  idx = index & 0x0F;
171141cc406Sopenharmony_ci  idx = idx << 4 | idx;
172141cc406Sopenharmony_ci  p5_outb (fd, EPPADR, idx);
173141cc406Sopenharmony_ci  return p5_inb (fd, EPPDATA);
174141cc406Sopenharmony_ci}
175141cc406Sopenharmony_ci
176141cc406Sopenharmony_ci#ifdef HAVE_LINUX_PPDEV_H
177141cc406Sopenharmony_cistatic int
178141cc406Sopenharmony_ciread_data (int fd, uint8_t * data, int length)
179141cc406Sopenharmony_ci{
180141cc406Sopenharmony_ci  int mode, rc, nb;
181141cc406Sopenharmony_ci  unsigned char bval;
182141cc406Sopenharmony_ci
183141cc406Sopenharmony_ci  bval = REG8;
184141cc406Sopenharmony_ci  mode = IEEE1284_MODE_EPP | IEEE1284_ADDR;
185141cc406Sopenharmony_ci  rc = ioctl (fd, PPSETMODE, &mode);
186141cc406Sopenharmony_ci  rc = write (fd, &bval, 1);
187141cc406Sopenharmony_ci
188141cc406Sopenharmony_ci  mode = 1;			/* data_reverse */
189141cc406Sopenharmony_ci  rc = ioctl (fd, PPDATADIR, &mode);
190141cc406Sopenharmony_ci#ifdef PPSETFLAGS
191141cc406Sopenharmony_ci  mode = PP_FASTREAD;
192141cc406Sopenharmony_ci  rc = ioctl (fd, PPSETFLAGS, &mode);
193141cc406Sopenharmony_ci#endif
194141cc406Sopenharmony_ci  mode = IEEE1284_MODE_EPP | IEEE1284_DATA;
195141cc406Sopenharmony_ci  rc = ioctl (fd, PPSETMODE, &mode);
196141cc406Sopenharmony_ci  nb = 0;
197141cc406Sopenharmony_ci  while (nb < length)
198141cc406Sopenharmony_ci    {
199141cc406Sopenharmony_ci      rc = read (fd, data + nb, length - nb);
200141cc406Sopenharmony_ci      if (rc < 0)
201141cc406Sopenharmony_ci	{
202141cc406Sopenharmony_ci	  DBG (DBG_error, "memtest: error reading data back!\n");
203141cc406Sopenharmony_ci	  return 0;
204141cc406Sopenharmony_ci	}
205141cc406Sopenharmony_ci      else
206141cc406Sopenharmony_ci	{
207141cc406Sopenharmony_ci	  nb += rc;
208141cc406Sopenharmony_ci	}
209141cc406Sopenharmony_ci    }
210141cc406Sopenharmony_ci
211141cc406Sopenharmony_ci  return 1;
212141cc406Sopenharmony_ci}
213141cc406Sopenharmony_ci
214141cc406Sopenharmony_cistatic void
215141cc406Sopenharmony_ciindex_write_data (int fd, uint8_t index, uint8_t * data, int length)
216141cc406Sopenharmony_ci{
217141cc406Sopenharmony_ci  int mode;
218141cc406Sopenharmony_ci  unsigned char bval;
219141cc406Sopenharmony_ci
220141cc406Sopenharmony_ci  bval = index;
221141cc406Sopenharmony_ci  mode = IEEE1284_MODE_EPP | IEEE1284_ADDR;
222141cc406Sopenharmony_ci  ioctl (fd, PPSETMODE, &mode);
223141cc406Sopenharmony_ci  write (fd, &bval, 1);
224141cc406Sopenharmony_ci
225141cc406Sopenharmony_ci  mode = IEEE1284_MODE_EPP | IEEE1284_DATA;
226141cc406Sopenharmony_ci  ioctl (fd, PPSETMODE, &mode);
227141cc406Sopenharmony_ci  mode = 0;			/* data forward */
228141cc406Sopenharmony_ci  ioctl (fd, PPDATADIR, &mode);
229141cc406Sopenharmony_ci  write (fd, data, length);
230141cc406Sopenharmony_ci  return;
231141cc406Sopenharmony_ci}
232141cc406Sopenharmony_ci
233141cc406Sopenharmony_cistatic void
234141cc406Sopenharmony_ciwrite_data (int fd, uint8_t * data, int length)
235141cc406Sopenharmony_ci{
236141cc406Sopenharmony_ci  index_write_data (fd, REG8, data, length);
237141cc406Sopenharmony_ci}
238141cc406Sopenharmony_ci
239141cc406Sopenharmony_cistatic void
240141cc406Sopenharmony_ciwrite_reg2 (int fd, uint8_t index, uint16_t value)
241141cc406Sopenharmony_ci{
242141cc406Sopenharmony_ci  uint8_t data2[2];
243141cc406Sopenharmony_ci
244141cc406Sopenharmony_ci  data2[0] = value & 0xff;
245141cc406Sopenharmony_ci  data2[1] = value >> 8;
246141cc406Sopenharmony_ci  index_write_data (fd, index, data2, 2);
247141cc406Sopenharmony_ci}
248141cc406Sopenharmony_ci#else
249141cc406Sopenharmony_ci
250141cc406Sopenharmony_cistatic int
251141cc406Sopenharmony_ciread_data (int fd, uint8_t * data, int length)
252141cc406Sopenharmony_ci{
253141cc406Sopenharmony_ci  if(fd && data && length)
254141cc406Sopenharmony_ci    return -1;
255141cc406Sopenharmony_ci  return -1;
256141cc406Sopenharmony_ci}
257141cc406Sopenharmony_ci
258141cc406Sopenharmony_cistatic void
259141cc406Sopenharmony_ciwrite_data (int fd, uint8_t * data, int length)
260141cc406Sopenharmony_ci{
261141cc406Sopenharmony_ci  if(fd && data && length)
262141cc406Sopenharmony_ci    return;
263141cc406Sopenharmony_ci}
264141cc406Sopenharmony_ci
265141cc406Sopenharmony_cistatic void
266141cc406Sopenharmony_ciwrite_reg2 (int fd, uint8_t index, uint16_t value)
267141cc406Sopenharmony_ci{
268141cc406Sopenharmony_ci  if(fd && index && value)
269141cc406Sopenharmony_ci    return;
270141cc406Sopenharmony_ci}
271141cc406Sopenharmony_ci#endif
272141cc406Sopenharmony_ci
273141cc406Sopenharmony_ci/**
274141cc406Sopenharmony_ci * @}
275141cc406Sopenharmony_ci */
276141cc406Sopenharmony_ci
277141cc406Sopenharmony_ci
278141cc406Sopenharmony_ci/** @brief This function checks a memory buffer.
279141cc406Sopenharmony_ci * This function writes at the given memory address then read it back
280141cc406Sopenharmony_ci * to check the scanner is correctly working.
281141cc406Sopenharmony_ci * @param fd file descriptor used to access hardware
282141cc406Sopenharmony_ci * @param addr address where to write and read
283141cc406Sopenharmony_ci * @return SANE_TRUE on success, SANE_FALSE otherwise
284141cc406Sopenharmony_ci */
285141cc406Sopenharmony_cistatic int
286141cc406Sopenharmony_cimemtest (int fd, uint16_t addr)
287141cc406Sopenharmony_ci{
288141cc406Sopenharmony_ci  uint8_t sent[256];
289141cc406Sopenharmony_ci  uint8_t back[256];
290141cc406Sopenharmony_ci  int i;
291141cc406Sopenharmony_ci
292141cc406Sopenharmony_ci  write_reg2 (fd, REG1, addr);
293141cc406Sopenharmony_ci  for (i = 0; i < 256; i++)
294141cc406Sopenharmony_ci    {
295141cc406Sopenharmony_ci      sent[i] = (uint8_t) i;
296141cc406Sopenharmony_ci      back[i] = 0;
297141cc406Sopenharmony_ci    }
298141cc406Sopenharmony_ci  write_data (fd, sent, 256);
299141cc406Sopenharmony_ci  read_data (fd, back, 256);
300141cc406Sopenharmony_ci
301141cc406Sopenharmony_ci  /* check if data read back is the same that the one sent */
302141cc406Sopenharmony_ci  for (i = 0; i < 256; i++)
303141cc406Sopenharmony_ci    {
304141cc406Sopenharmony_ci      if (back[i] != sent[i])
305141cc406Sopenharmony_ci	{
306141cc406Sopenharmony_ci	  return SANE_FALSE;
307141cc406Sopenharmony_ci	}
308141cc406Sopenharmony_ci    }
309141cc406Sopenharmony_ci
310141cc406Sopenharmony_ci  return SANE_TRUE;
311141cc406Sopenharmony_ci}
312141cc406Sopenharmony_ci
313141cc406Sopenharmony_ci
314141cc406Sopenharmony_ci#define P5_INB(k,y,z) val=p5_inb(k,y); if(val!=z) { DBG(DBG_error,"expected 0x%02x, got 0x%02x\n",z, val); return SANE_FALSE; }
315141cc406Sopenharmony_ci
316141cc406Sopenharmony_ci/** @brief connect to scanner
317141cc406Sopenharmony_ci * This function sends the connect sequence for the scanner.
318141cc406Sopenharmony_ci * @param fd filedescriptor of the parallel port communication channel
319141cc406Sopenharmony_ci * @return SANE_TRUE in case of success, SANE_FALSE otherwise
320141cc406Sopenharmony_ci */
321141cc406Sopenharmony_cistatic int
322141cc406Sopenharmony_ciconnect (int fd)
323141cc406Sopenharmony_ci{
324141cc406Sopenharmony_ci  uint8_t val;
325141cc406Sopenharmony_ci
326141cc406Sopenharmony_ci  p5_inb (fd, CONTROL);
327141cc406Sopenharmony_ci  p5_outb (fd, CONTROL, 0x04);
328141cc406Sopenharmony_ci  p5_outb (fd, DATA, 0x02);
329141cc406Sopenharmony_ci  P5_INB (fd, DATA, 0x02);
330141cc406Sopenharmony_ci  p5_outb (fd, DATA, 0x03);
331141cc406Sopenharmony_ci  P5_INB (fd, DATA, 0x03);
332141cc406Sopenharmony_ci  p5_outb (fd, DATA, 0x03);
333141cc406Sopenharmony_ci  p5_outb (fd, DATA, 0x83);
334141cc406Sopenharmony_ci  p5_outb (fd, DATA, 0x03);
335141cc406Sopenharmony_ci  p5_outb (fd, DATA, 0x83);
336141cc406Sopenharmony_ci  P5_INB (fd, DATA, 0x83);
337141cc406Sopenharmony_ci  p5_outb (fd, DATA, 0x82);
338141cc406Sopenharmony_ci  P5_INB (fd, DATA, 0x82);
339141cc406Sopenharmony_ci  p5_outb (fd, DATA, 0x02);
340141cc406Sopenharmony_ci  p5_outb (fd, DATA, 0x82);
341141cc406Sopenharmony_ci  p5_outb (fd, DATA, 0x02);
342141cc406Sopenharmony_ci  p5_outb (fd, DATA, 0x82);
343141cc406Sopenharmony_ci  P5_INB (fd, DATA, 0x82);
344141cc406Sopenharmony_ci  p5_outb (fd, DATA, 0x82);
345141cc406Sopenharmony_ci  P5_INB (fd, DATA, 0x82);
346141cc406Sopenharmony_ci  p5_outb (fd, DATA, 0x02);
347141cc406Sopenharmony_ci  p5_outb (fd, DATA, 0x82);
348141cc406Sopenharmony_ci  p5_outb (fd, DATA, 0x02);
349141cc406Sopenharmony_ci  p5_outb (fd, DATA, 0x82);
350141cc406Sopenharmony_ci  P5_INB (fd, DATA, 0x82);
351141cc406Sopenharmony_ci  p5_outb (fd, DATA, 0x83);
352141cc406Sopenharmony_ci  P5_INB (fd, DATA, 0x83);
353141cc406Sopenharmony_ci  p5_outb (fd, DATA, 0x03);
354141cc406Sopenharmony_ci  p5_outb (fd, DATA, 0x83);
355141cc406Sopenharmony_ci  p5_outb (fd, DATA, 0x03);
356141cc406Sopenharmony_ci  p5_outb (fd, DATA, 0x83);
357141cc406Sopenharmony_ci  P5_INB (fd, DATA, 0x83);
358141cc406Sopenharmony_ci  p5_outb (fd, DATA, 0x82);
359141cc406Sopenharmony_ci  P5_INB (fd, DATA, 0x82);
360141cc406Sopenharmony_ci  p5_outb (fd, DATA, 0x02);
361141cc406Sopenharmony_ci  p5_outb (fd, DATA, 0x82);
362141cc406Sopenharmony_ci  p5_outb (fd, DATA, 0x02);
363141cc406Sopenharmony_ci  p5_outb (fd, DATA, 0x82);
364141cc406Sopenharmony_ci  P5_INB (fd, DATA, 0x82);
365141cc406Sopenharmony_ci  p5_outb (fd, DATA, 0x83);
366141cc406Sopenharmony_ci  P5_INB (fd, DATA, 0x83);
367141cc406Sopenharmony_ci  p5_outb (fd, DATA, 0x03);
368141cc406Sopenharmony_ci  p5_outb (fd, DATA, 0x83);
369141cc406Sopenharmony_ci  p5_outb (fd, DATA, 0x03);
370141cc406Sopenharmony_ci  p5_outb (fd, DATA, 0x83);
371141cc406Sopenharmony_ci  P5_INB (fd, DATA, 0x83);
372141cc406Sopenharmony_ci  p5_outb (fd, DATA, 0x83);
373141cc406Sopenharmony_ci  P5_INB (fd, DATA, 0x83);
374141cc406Sopenharmony_ci  p5_outb (fd, DATA, 0x03);
375141cc406Sopenharmony_ci  p5_outb (fd, DATA, 0x83);
376141cc406Sopenharmony_ci  p5_outb (fd, DATA, 0x03);
377141cc406Sopenharmony_ci  p5_outb (fd, DATA, 0x83);
378141cc406Sopenharmony_ci  P5_INB (fd, DATA, 0x83);
379141cc406Sopenharmony_ci  p5_outb (fd, DATA, 0x82);
380141cc406Sopenharmony_ci  P5_INB (fd, DATA, 0x82);
381141cc406Sopenharmony_ci  p5_outb (fd, DATA, 0x02);
382141cc406Sopenharmony_ci  p5_outb (fd, DATA, 0x82);
383141cc406Sopenharmony_ci  p5_outb (fd, DATA, 0x02);
384141cc406Sopenharmony_ci  p5_outb (fd, DATA, 0x82);
385141cc406Sopenharmony_ci  p5_outb (fd, DATA, 0xFF);
386141cc406Sopenharmony_ci  DBG (DBG_info, "connect() OK...\n");
387141cc406Sopenharmony_ci  return SANE_TRUE;
388141cc406Sopenharmony_ci}
389141cc406Sopenharmony_ci
390141cc406Sopenharmony_cistatic int
391141cc406Sopenharmony_cidisconnect (int fd)
392141cc406Sopenharmony_ci{
393141cc406Sopenharmony_ci  uint8_t val;
394141cc406Sopenharmony_ci
395141cc406Sopenharmony_ci  p5_outb (fd, CONTROL, 0x04);
396141cc406Sopenharmony_ci  p5_outb (fd, DATA, 0x00);
397141cc406Sopenharmony_ci  P5_INB (fd, DATA, 0x00);
398141cc406Sopenharmony_ci  p5_outb (fd, DATA, 0x01);
399141cc406Sopenharmony_ci  P5_INB (fd, DATA, 0x01);
400141cc406Sopenharmony_ci  p5_outb (fd, DATA, 0x01);
401141cc406Sopenharmony_ci  p5_outb (fd, DATA, 0x81);
402141cc406Sopenharmony_ci  p5_outb (fd, DATA, 0x01);
403141cc406Sopenharmony_ci  p5_outb (fd, DATA, 0x81);
404141cc406Sopenharmony_ci  P5_INB (fd, DATA, 0x81);
405141cc406Sopenharmony_ci  p5_outb (fd, DATA, 0x80);
406141cc406Sopenharmony_ci  P5_INB (fd, DATA, 0x80);
407141cc406Sopenharmony_ci  p5_outb (fd, DATA, 0x00);
408141cc406Sopenharmony_ci  p5_outb (fd, DATA, 0x80);
409141cc406Sopenharmony_ci  p5_outb (fd, DATA, 0x00);
410141cc406Sopenharmony_ci  p5_outb (fd, DATA, 0x80);
411141cc406Sopenharmony_ci  P5_INB (fd, DATA, 0x80);
412141cc406Sopenharmony_ci  p5_outb (fd, DATA, 0x80);
413141cc406Sopenharmony_ci  P5_INB (fd, DATA, 0x80);
414141cc406Sopenharmony_ci  p5_outb (fd, DATA, 0x00);
415141cc406Sopenharmony_ci  p5_outb (fd, DATA, 0x80);
416141cc406Sopenharmony_ci  p5_outb (fd, DATA, 0x00);
417141cc406Sopenharmony_ci  p5_outb (fd, DATA, 0x80);
418141cc406Sopenharmony_ci  P5_INB (fd, DATA, 0x80);
419141cc406Sopenharmony_ci  p5_outb (fd, DATA, 0x81);
420141cc406Sopenharmony_ci  P5_INB (fd, DATA, 0x81);
421141cc406Sopenharmony_ci  p5_outb (fd, DATA, 0x01);
422141cc406Sopenharmony_ci  p5_outb (fd, DATA, 0x81);
423141cc406Sopenharmony_ci  p5_outb (fd, DATA, 0x01);
424141cc406Sopenharmony_ci  p5_outb (fd, DATA, 0x81);
425141cc406Sopenharmony_ci  P5_INB (fd, DATA, 0x81);
426141cc406Sopenharmony_ci  p5_outb (fd, DATA, 0x80);
427141cc406Sopenharmony_ci  P5_INB (fd, DATA, 0x80);
428141cc406Sopenharmony_ci  p5_outb (fd, DATA, 0x00);
429141cc406Sopenharmony_ci  p5_outb (fd, DATA, 0x80);
430141cc406Sopenharmony_ci  p5_outb (fd, DATA, 0x00);
431141cc406Sopenharmony_ci  p5_outb (fd, DATA, 0x80);
432141cc406Sopenharmony_ci  P5_INB (fd, DATA, 0x80);
433141cc406Sopenharmony_ci  p5_outb (fd, DATA, 0x00);
434141cc406Sopenharmony_ci  p5_outb (fd, DATA, 0x80);
435141cc406Sopenharmony_ci  p5_outb (fd, DATA, 0x00);
436141cc406Sopenharmony_ci  p5_outb (fd, DATA, 0x80);
437141cc406Sopenharmony_ci  P5_INB (fd, DATA, 0x80);
438141cc406Sopenharmony_ci  p5_outb (fd, DATA, 0x00);
439141cc406Sopenharmony_ci  p5_outb (fd, DATA, 0x80);
440141cc406Sopenharmony_ci  p5_outb (fd, DATA, 0x00);
441141cc406Sopenharmony_ci  p5_outb (fd, DATA, 0x80);
442141cc406Sopenharmony_ci  P5_INB (fd, DATA, 0x80);
443141cc406Sopenharmony_ci  p5_outb (fd, DATA, 0x00);
444141cc406Sopenharmony_ci  p5_outb (fd, DATA, 0x80);
445141cc406Sopenharmony_ci  p5_outb (fd, DATA, 0x00);
446141cc406Sopenharmony_ci  p5_outb (fd, DATA, 0x80);
447141cc406Sopenharmony_ci  p5_inb (fd, CONTROL);
448141cc406Sopenharmony_ci  p5_outb (fd, CONTROL, 0x0C);
449141cc406Sopenharmony_ci
450141cc406Sopenharmony_ci  return SANE_STATUS_GOOD;
451141cc406Sopenharmony_ci}
452141cc406Sopenharmony_ci
453141cc406Sopenharmony_cistatic void
454141cc406Sopenharmony_cisetadresses (int fd, uint16_t start, uint16_t end)
455141cc406Sopenharmony_ci{
456141cc406Sopenharmony_ci  write_reg (fd, REG3, start & 0xff);
457141cc406Sopenharmony_ci  write_reg (fd, REG4, start >> 8);
458141cc406Sopenharmony_ci  write_reg (fd, REG5, end & 0xff);
459141cc406Sopenharmony_ci  write_reg (fd, REG6, end >> 8);
460141cc406Sopenharmony_ci  DBG (DBG_io, "setadresses(0x%x,0x%x); OK...\n", start, end);
461141cc406Sopenharmony_ci}
462141cc406Sopenharmony_ci
463141cc406Sopenharmony_ci#ifdef HAVE_LINUX_PPDEV_H
464141cc406Sopenharmony_ci/** @brief open parallel port device
465141cc406Sopenharmony_ci * opens parallel port's low level device in EPP mode
466141cc406Sopenharmony_ci * @param devicename nam of the real device or the special value 'auto'
467141cc406Sopenharmony_ci * @return file descriptor in cas of successn -1 otherwise
468141cc406Sopenharmony_ci */
469141cc406Sopenharmony_cistatic int
470141cc406Sopenharmony_ciopen_pp (const char *devicename)
471141cc406Sopenharmony_ci{
472141cc406Sopenharmony_ci  int fd, mode = 0;
473141cc406Sopenharmony_ci  char *name;
474141cc406Sopenharmony_ci
475141cc406Sopenharmony_ci  DBG (DBG_proc, "open_pp: start, devicename=%s\n", devicename);
476141cc406Sopenharmony_ci  /* TODO improve auto device finding */
477141cc406Sopenharmony_ci  if (strncmp (devicename, "auto", 4) == 0)
478141cc406Sopenharmony_ci    {
479141cc406Sopenharmony_ci      name = strdup("/dev/parport0");
480141cc406Sopenharmony_ci    }
481141cc406Sopenharmony_ci  else
482141cc406Sopenharmony_ci    {
483141cc406Sopenharmony_ci      name = strdup(devicename);
484141cc406Sopenharmony_ci    }
485141cc406Sopenharmony_ci
486141cc406Sopenharmony_ci  /* open device */
487141cc406Sopenharmony_ci  fd = open (name, O_RDWR);
488141cc406Sopenharmony_ci  if (fd < 0)
489141cc406Sopenharmony_ci    {
490141cc406Sopenharmony_ci      switch (errno)
491141cc406Sopenharmony_ci	{
492141cc406Sopenharmony_ci	case ENOENT:
493141cc406Sopenharmony_ci#ifdef ENIO
494141cc406Sopenharmony_ci	case ENXIO:
495141cc406Sopenharmony_ci#endif
496141cc406Sopenharmony_ci#ifdef ENODEV
497141cc406Sopenharmony_ci	case ENODEV:
498141cc406Sopenharmony_ci#endif
499141cc406Sopenharmony_ci	  DBG (DBG_error, "open_pp: no %s device ...\n", name);
500141cc406Sopenharmony_ci	  break;
501141cc406Sopenharmony_ci	case EACCES:
502141cc406Sopenharmony_ci	  DBG (DBG_error,
503141cc406Sopenharmony_ci	       "open_pp: current user cannot use existing %s device ...\n",
504141cc406Sopenharmony_ci	       name);
505141cc406Sopenharmony_ci	  break;
506141cc406Sopenharmony_ci	default:
507141cc406Sopenharmony_ci	  DBG (DBG_error, "open_pp: %s while opening %s\n", strerror (errno),
508141cc406Sopenharmony_ci	       name);
509141cc406Sopenharmony_ci	}
510141cc406Sopenharmony_ci      return -1;
511141cc406Sopenharmony_ci    }
512141cc406Sopenharmony_ci  free(name);
513141cc406Sopenharmony_ci
514141cc406Sopenharmony_ci  /* claim device and set it to EPP */
515141cc406Sopenharmony_ci  ioctl (fd, PPCLAIM);
516141cc406Sopenharmony_ci  ioctl (fd, PPGETMODES, &mode);
517141cc406Sopenharmony_ci  if (mode & PARPORT_MODE_PCSPP)
518141cc406Sopenharmony_ci    DBG (DBG_io, "PARPORT_MODE_PCSPP\n");
519141cc406Sopenharmony_ci  if (mode & PARPORT_MODE_TRISTATE)
520141cc406Sopenharmony_ci    DBG (DBG_io, "PARPORT_MODE_TRISTATE\n");
521141cc406Sopenharmony_ci  if (mode & PARPORT_MODE_EPP)
522141cc406Sopenharmony_ci    DBG (DBG_io, "PARPORT_MODE_EPP\n");
523141cc406Sopenharmony_ci  if (mode & PARPORT_MODE_ECP)
524141cc406Sopenharmony_ci    DBG (DBG_io, "PARPORT_MODE_ECP\n");
525141cc406Sopenharmony_ci  if (mode & PARPORT_MODE_COMPAT)
526141cc406Sopenharmony_ci    DBG (DBG_io, "PARPORT_MODE_COMPAT\n");
527141cc406Sopenharmony_ci  if (mode & PARPORT_MODE_DMA)
528141cc406Sopenharmony_ci    DBG (DBG_io, "PARPORT_MODE_DMA\n");
529141cc406Sopenharmony_ci  if (mode & PARPORT_MODE_EPP)
530141cc406Sopenharmony_ci    {
531141cc406Sopenharmony_ci      mode = IEEE1284_MODE_EPP;
532141cc406Sopenharmony_ci    }
533141cc406Sopenharmony_ci  else
534141cc406Sopenharmony_ci    {
535141cc406Sopenharmony_ci      /*
536141cc406Sopenharmony_ci         if (mode & PARPORT_MODE_ECP)
537141cc406Sopenharmony_ci         {
538141cc406Sopenharmony_ci         mode = IEEE1284_MODE_ECP;
539141cc406Sopenharmony_ci         }
540141cc406Sopenharmony_ci         else
541141cc406Sopenharmony_ci       */
542141cc406Sopenharmony_ci      {
543141cc406Sopenharmony_ci	mode = -1;
544141cc406Sopenharmony_ci      }
545141cc406Sopenharmony_ci    }
546141cc406Sopenharmony_ci  if (mode == -1)
547141cc406Sopenharmony_ci    {
548141cc406Sopenharmony_ci      DBG (DBG_error, "open_pp: no EPP mode, giving up ...\n");
549141cc406Sopenharmony_ci      ioctl (fd, PPRELEASE);
550141cc406Sopenharmony_ci      close (fd);
551141cc406Sopenharmony_ci      return -1;
552141cc406Sopenharmony_ci    }
553141cc406Sopenharmony_ci  ioctl (fd, PPNEGOT, &mode);
554141cc406Sopenharmony_ci  ioctl (fd, PPSETMODE, &mode);
555141cc406Sopenharmony_ci  DBG (DBG_proc, "open_pp: exit\n");
556141cc406Sopenharmony_ci  return fd;
557141cc406Sopenharmony_ci}
558141cc406Sopenharmony_ci
559141cc406Sopenharmony_ci/** close low level device
560141cc406Sopenharmony_ci * release and close low level hardware device
561141cc406Sopenharmony_ci */
562141cc406Sopenharmony_cistatic void
563141cc406Sopenharmony_ciclose_pp (int fd)
564141cc406Sopenharmony_ci{
565141cc406Sopenharmony_ci  int mode = IEEE1284_MODE_COMPAT;
566141cc406Sopenharmony_ci
567141cc406Sopenharmony_ci  if (fd > 2)
568141cc406Sopenharmony_ci    {
569141cc406Sopenharmony_ci      ioctl (fd, PPNEGOT, &mode);
570141cc406Sopenharmony_ci      ioctl (fd, PPRELEASE);
571141cc406Sopenharmony_ci      close (fd);
572141cc406Sopenharmony_ci    }
573141cc406Sopenharmony_ci}
574141cc406Sopenharmony_ci
575141cc406Sopenharmony_ci#else /*  HAVE_LINUX_PPDEV_H */
576141cc406Sopenharmony_ci
577141cc406Sopenharmony_cistatic int
578141cc406Sopenharmony_ciopen_pp (const char *devicename)
579141cc406Sopenharmony_ci{
580141cc406Sopenharmony_ci  if(devicename)
581141cc406Sopenharmony_ci    return -1;
582141cc406Sopenharmony_ci  return -1;
583141cc406Sopenharmony_ci}
584141cc406Sopenharmony_ci
585141cc406Sopenharmony_cistatic void
586141cc406Sopenharmony_ciclose_pp (int fd)
587141cc406Sopenharmony_ci{
588141cc406Sopenharmony_ci  if(fd)
589141cc406Sopenharmony_ci    return;
590141cc406Sopenharmony_ci}
591141cc406Sopenharmony_ci#endif /*  HAVE_LINUX_PPDEV_H */
592141cc406Sopenharmony_ci
593141cc406Sopenharmony_ci/** @brief test if a document is inserted
594141cc406Sopenharmony_ci * Test if a document is inserted by reading register E
595141cc406Sopenharmony_ci * @param fd file descriptor to access scanner
596141cc406Sopenharmony_ci * @return SANE_STATUS_NO_DOCS if no document or SANE_STATUS_GOOD
597141cc406Sopenharmony_ci * if something is present.
598141cc406Sopenharmony_ci */
599141cc406Sopenharmony_cistatic SANE_Status
600141cc406Sopenharmony_citest_document (int fd)
601141cc406Sopenharmony_ci{
602141cc406Sopenharmony_ci  int detector;
603141cc406Sopenharmony_ci
604141cc406Sopenharmony_ci  /* check for document presence 0xC6: present, 0xC3 no document */
605141cc406Sopenharmony_ci  detector = read_reg (fd, REGE);
606141cc406Sopenharmony_ci  DBG (DBG_io, "test_document: detector=0x%02X\n", detector);
607141cc406Sopenharmony_ci
608141cc406Sopenharmony_ci  /* document inserted */
609141cc406Sopenharmony_ci  if (detector & 0x04)
610141cc406Sopenharmony_ci    return SANE_STATUS_GOOD;
611141cc406Sopenharmony_ci
612141cc406Sopenharmony_ci  return SANE_STATUS_NO_DOCS;
613141cc406Sopenharmony_ci}
614141cc406Sopenharmony_ci
615141cc406Sopenharmony_ci/**
616141cc406Sopenharmony_ci * return the amount of scanned data available
617141cc406Sopenharmony_ci * @param fd file descriptor to access scanner
618141cc406Sopenharmony_ci * @return available byte number
619141cc406Sopenharmony_ci */
620141cc406Sopenharmony_cistatic int
621141cc406Sopenharmony_ciavailable_bytes (int fd)
622141cc406Sopenharmony_ci{
623141cc406Sopenharmony_ci  int counter;
624141cc406Sopenharmony_ci
625141cc406Sopenharmony_ci  /* read the number of 256 bytes block of scanned data */
626141cc406Sopenharmony_ci  counter = read_reg (fd, REG9);
627141cc406Sopenharmony_ci  DBG (DBG_io, "available_bytes: available_bytes=0x%02X\n", counter);
628141cc406Sopenharmony_ci  return 256 * counter;
629141cc406Sopenharmony_ci}
630141cc406Sopenharmony_ci
631141cc406Sopenharmony_cistatic SANE_Status
632141cc406Sopenharmony_cibuild_correction (P5_Device * dev, unsigned int dpi, unsigned int mode,
633141cc406Sopenharmony_ci		  unsigned int start, unsigned int width)
634141cc406Sopenharmony_ci{
635141cc406Sopenharmony_ci  unsigned int i, j, shift, step;
636141cc406Sopenharmony_ci
637141cc406Sopenharmony_ci  DBG (DBG_proc, "build_correction: start=%d, width=%d\n", start, width);
638141cc406Sopenharmony_ci  DBG (DBG_trace, "build_correction: dpi=%d, mode=%d\n", dpi, mode);
639141cc406Sopenharmony_ci
640141cc406Sopenharmony_ci  /* loop on calibration data to find the matching one */
641141cc406Sopenharmony_ci  j = 0;
642141cc406Sopenharmony_ci  while (dev->calibration_data[j]->dpi != dpi)
643141cc406Sopenharmony_ci    {
644141cc406Sopenharmony_ci      j++;
645141cc406Sopenharmony_ci      if (j > MAX_RESOLUTIONS)
646141cc406Sopenharmony_ci	{
647141cc406Sopenharmony_ci	  DBG (DBG_error, "build_correction: couldn't find calibration!\n");
648141cc406Sopenharmony_ci	  return SANE_STATUS_INVAL;
649141cc406Sopenharmony_ci	}
650141cc406Sopenharmony_ci    }
651141cc406Sopenharmony_ci
652141cc406Sopenharmony_ci  if (dev->gain != NULL)
653141cc406Sopenharmony_ci    {
654141cc406Sopenharmony_ci      free (dev->gain);
655141cc406Sopenharmony_ci      dev->gain = NULL;
656141cc406Sopenharmony_ci    }
657141cc406Sopenharmony_ci  if (dev->offset != NULL)
658141cc406Sopenharmony_ci    {
659141cc406Sopenharmony_ci      free (dev->offset);
660141cc406Sopenharmony_ci      dev->offset = NULL;
661141cc406Sopenharmony_ci    }
662141cc406Sopenharmony_ci  dev->gain = (float *) malloc (width * sizeof (float));
663141cc406Sopenharmony_ci  if (dev->gain == NULL)
664141cc406Sopenharmony_ci    {
665141cc406Sopenharmony_ci      DBG (DBG_error,
666141cc406Sopenharmony_ci	   "build_correction: failed to allocate memory for gain!\n");
667141cc406Sopenharmony_ci      return SANE_STATUS_NO_MEM;
668141cc406Sopenharmony_ci    }
669141cc406Sopenharmony_ci  dev->offset = (uint8_t *) malloc (width);
670141cc406Sopenharmony_ci  if (dev->offset == NULL)
671141cc406Sopenharmony_ci    {
672141cc406Sopenharmony_ci      DBG (DBG_error,
673141cc406Sopenharmony_ci	   "build_correction: failed to allocate memory for offset!\n");
674141cc406Sopenharmony_ci      return SANE_STATUS_NO_MEM;
675141cc406Sopenharmony_ci    }
676141cc406Sopenharmony_ci
677141cc406Sopenharmony_ci  /* compute starting point of calibration data to use */
678141cc406Sopenharmony_ci  shift = start;
679141cc406Sopenharmony_ci  step = 1;
680141cc406Sopenharmony_ci  if (mode == MODE_GRAY)
681141cc406Sopenharmony_ci    {
682141cc406Sopenharmony_ci      /* we use green data */
683141cc406Sopenharmony_ci      shift += 1;
684141cc406Sopenharmony_ci      step = 3;
685141cc406Sopenharmony_ci    }
686141cc406Sopenharmony_ci  for (i = 0; i < width; i += step)
687141cc406Sopenharmony_ci    {
688141cc406Sopenharmony_ci      if (dev->calibration_data[j]->white_data[shift + i] -
689141cc406Sopenharmony_ci	  dev->calibration_data[0]->black_data[shift + i] > BLACK_LEVEL)
690141cc406Sopenharmony_ci	{
691141cc406Sopenharmony_ci	  dev->gain[i] =
692141cc406Sopenharmony_ci	    WHITE_TARGET /
693141cc406Sopenharmony_ci	    ((float)
694141cc406Sopenharmony_ci	     (dev->calibration_data[j]->white_data[shift + i] -
695141cc406Sopenharmony_ci	      dev->calibration_data[j]->black_data[shift + i]));
696141cc406Sopenharmony_ci	  dev->offset[i] = dev->calibration_data[j]->black_data[shift + i];
697141cc406Sopenharmony_ci	}
698141cc406Sopenharmony_ci      else
699141cc406Sopenharmony_ci	{
700141cc406Sopenharmony_ci	  dev->gain[i] = 1.0;
701141cc406Sopenharmony_ci	  dev->offset[i] = 0;
702141cc406Sopenharmony_ci	}
703141cc406Sopenharmony_ci    }
704141cc406Sopenharmony_ci  return SANE_STATUS_GOOD;
705141cc406Sopenharmony_ci  DBG (DBG_proc, "build_correction: end\n");
706141cc406Sopenharmony_ci}
707141cc406Sopenharmony_ci
708141cc406Sopenharmony_ci/** @brief start up a real scan
709141cc406Sopenharmony_ci * This function starts the scan with the given parameters.
710141cc406Sopenharmony_ci * @param dev device describing hardware
711141cc406Sopenharmony_ci * @param mode color, gray level or lineart.
712141cc406Sopenharmony_ci * @param dpi desired scan resolution.
713141cc406Sopenharmony_ci * @param startx coordinate of the first pixel to scan in
714141cc406Sopenharmony_ci * scan's resolution coordinate
715141cc406Sopenharmony_ci * @param width width of the scanned area
716141cc406Sopenharmony_ci * scanner's physical scan aread.
717141cc406Sopenharmony_ci * @return SANE_STATUS_GOOD if scan is successfully started
718141cc406Sopenharmony_ci */
719141cc406Sopenharmony_cistatic SANE_Status
720141cc406Sopenharmony_cistart_scan (P5_Device * dev, int mode, unsigned int dpi, unsigned int startx,
721141cc406Sopenharmony_ci	    unsigned int width)
722141cc406Sopenharmony_ci{
723141cc406Sopenharmony_ci  uint8_t reg0=0;
724141cc406Sopenharmony_ci  uint8_t reg2=0;
725141cc406Sopenharmony_ci  uint8_t regF=0;
726141cc406Sopenharmony_ci  uint16_t addr=0;
727141cc406Sopenharmony_ci  uint16_t start, end;
728141cc406Sopenharmony_ci  unsigned int xdpi;
729141cc406Sopenharmony_ci
730141cc406Sopenharmony_ci  DBG (DBG_proc, "start_scan: start \n");
731141cc406Sopenharmony_ci  DBG (DBG_io, "start_scan: startx=%d, width=%d, dpi=%d\n", startx, width,
732141cc406Sopenharmony_ci       dpi);
733141cc406Sopenharmony_ci
734141cc406Sopenharmony_ci	/** @brief register values
735141cc406Sopenharmony_ci	 * - reg2 : reg2 seems related to x dpi and provides only 100,
736141cc406Sopenharmony_ci	 *   150, 200 and 300 resolutions.
737141cc406Sopenharmony_ci	 * - regF : lower nibble gives y dpi resolution ranging from 150
738141cc406Sopenharmony_ci	 *   to 1200 dpi.
739141cc406Sopenharmony_ci	 */
740141cc406Sopenharmony_ci  xdpi = dpi;
741141cc406Sopenharmony_ci  switch (dpi)
742141cc406Sopenharmony_ci    {
743141cc406Sopenharmony_ci    case 100:
744141cc406Sopenharmony_ci      reg2 = 0x90;
745141cc406Sopenharmony_ci      regF = 0xA2;
746141cc406Sopenharmony_ci      break;
747141cc406Sopenharmony_ci    case 150:
748141cc406Sopenharmony_ci      reg2 = 0x10;
749141cc406Sopenharmony_ci      regF = 0xA4;
750141cc406Sopenharmony_ci      break;
751141cc406Sopenharmony_ci    case 200:
752141cc406Sopenharmony_ci      reg2 = 0x80;
753141cc406Sopenharmony_ci      regF = 0xA6;
754141cc406Sopenharmony_ci      break;
755141cc406Sopenharmony_ci    case 300:
756141cc406Sopenharmony_ci      reg2 = 0x00;
757141cc406Sopenharmony_ci      regF = 0xA8;
758141cc406Sopenharmony_ci      break;
759141cc406Sopenharmony_ci    case 400:
760141cc406Sopenharmony_ci      reg2 = 0x80;		/* xdpi=200 */
761141cc406Sopenharmony_ci      regF = 0xAA;
762141cc406Sopenharmony_ci      xdpi = 200;
763141cc406Sopenharmony_ci      break;
764141cc406Sopenharmony_ci    case 500:
765141cc406Sopenharmony_ci      reg2 = 0x00;
766141cc406Sopenharmony_ci      regF = 0xAC;
767141cc406Sopenharmony_ci      xdpi = 300;
768141cc406Sopenharmony_ci      break;
769141cc406Sopenharmony_ci    case 600:
770141cc406Sopenharmony_ci      reg2 = 0x00;
771141cc406Sopenharmony_ci      regF = 0xAE;
772141cc406Sopenharmony_ci      xdpi = 300;
773141cc406Sopenharmony_ci      break;
774141cc406Sopenharmony_ci    }
775141cc406Sopenharmony_ci
776141cc406Sopenharmony_ci  switch (mode)
777141cc406Sopenharmony_ci    {
778141cc406Sopenharmony_ci    case MODE_COLOR:
779141cc406Sopenharmony_ci      reg0 = 0x00;
780141cc406Sopenharmony_ci      addr = 0x0100;
781141cc406Sopenharmony_ci      break;
782141cc406Sopenharmony_ci    case MODE_GRAY:
783141cc406Sopenharmony_ci      /* green channel only */
784141cc406Sopenharmony_ci      reg0 = 0x20;
785141cc406Sopenharmony_ci      addr = 0x0100;
786141cc406Sopenharmony_ci      break;
787141cc406Sopenharmony_ci    case MODE_LINEART:
788141cc406Sopenharmony_ci      reg0 = 0x40;
789141cc406Sopenharmony_ci      addr = 0x0908;
790141cc406Sopenharmony_ci      break;
791141cc406Sopenharmony_ci    }
792141cc406Sopenharmony_ci
793141cc406Sopenharmony_ci  write_reg (dev->fd, REG1, 0x01);
794141cc406Sopenharmony_ci  write_reg (dev->fd, REG7, 0x00);
795141cc406Sopenharmony_ci  write_reg (dev->fd, REG0, reg0);
796141cc406Sopenharmony_ci  write_reg (dev->fd, REG1, 0x00);
797141cc406Sopenharmony_ci  write_reg (dev->fd, REGF, regF);
798141cc406Sopenharmony_ci  /* the memory addr used to test need not to be related
799141cc406Sopenharmony_ci   * to resolution, 0x0100 could be always used */
800141cc406Sopenharmony_ci  /* TODO get rid of it */
801141cc406Sopenharmony_ci  memtest (dev->fd, addr);
802141cc406Sopenharmony_ci
803141cc406Sopenharmony_ci  /* handle case where dpi>xdpi */
804141cc406Sopenharmony_ci  start = startx;
805141cc406Sopenharmony_ci  if (dpi > xdpi)
806141cc406Sopenharmony_ci    {
807141cc406Sopenharmony_ci      width = (width * xdpi) / dpi;
808141cc406Sopenharmony_ci      start = (startx * xdpi) / dpi;
809141cc406Sopenharmony_ci    }
810141cc406Sopenharmony_ci
811141cc406Sopenharmony_ci  /* compute and set start addr */
812141cc406Sopenharmony_ci  if (mode == MODE_COLOR)
813141cc406Sopenharmony_ci    {
814141cc406Sopenharmony_ci      start = start * 3;
815141cc406Sopenharmony_ci      width = width * 3;
816141cc406Sopenharmony_ci    }
817141cc406Sopenharmony_ci  end = start + width + 1;
818141cc406Sopenharmony_ci
819141cc406Sopenharmony_ci  /* build calibration data for the scan */
820141cc406Sopenharmony_ci  if (dev->calibrated)
821141cc406Sopenharmony_ci    {
822141cc406Sopenharmony_ci      build_correction (dev, xdpi, mode, start, width);
823141cc406Sopenharmony_ci    }
824141cc406Sopenharmony_ci
825141cc406Sopenharmony_ci  setadresses (dev->fd, start, end);
826141cc406Sopenharmony_ci
827141cc406Sopenharmony_ci  write_reg (dev->fd, REG1, addr >> 8);
828141cc406Sopenharmony_ci  write_reg (dev->fd, REG2, reg2);
829141cc406Sopenharmony_ci  regF = (regF & 0x0F) | 0x80;
830141cc406Sopenharmony_ci  write_reg (dev->fd, REGF, regF);
831141cc406Sopenharmony_ci  write_reg (dev->fd, REG0, reg0);
832141cc406Sopenharmony_ci  if (mode == MODE_LINEART)
833141cc406Sopenharmony_ci    {
834141cc406Sopenharmony_ci      write_reg (dev->fd, 0x07, 0x04);
835141cc406Sopenharmony_ci    }
836141cc406Sopenharmony_ci  else
837141cc406Sopenharmony_ci    {
838141cc406Sopenharmony_ci      write_reg (dev->fd, 0x07, 0x00);
839141cc406Sopenharmony_ci    }
840141cc406Sopenharmony_ci  write_reg (dev->fd, REG1, addr >> 8);
841141cc406Sopenharmony_ci  write_reg2 (dev->fd, REG1, addr);
842141cc406Sopenharmony_ci  write_reg (dev->fd, REGF, regF | 0x01);
843141cc406Sopenharmony_ci  write_reg (dev->fd, REG0, reg0 | 0x0C);
844141cc406Sopenharmony_ci  if (mode == MODE_LINEART)
845141cc406Sopenharmony_ci    {
846141cc406Sopenharmony_ci      write_reg (dev->fd, REG1, 0x19);
847141cc406Sopenharmony_ci    }
848141cc406Sopenharmony_ci  else
849141cc406Sopenharmony_ci    {
850141cc406Sopenharmony_ci      write_reg (dev->fd, REG1, 0x11);
851141cc406Sopenharmony_ci    }
852141cc406Sopenharmony_ci  DBG (DBG_proc, "start_scan: exit\n");
853141cc406Sopenharmony_ci  return SANE_STATUS_GOOD;
854141cc406Sopenharmony_ci}
855141cc406Sopenharmony_ci
856141cc406Sopenharmony_ci/** read a line of scan data
857141cc406Sopenharmony_ci * @param dev device to read
858141cc406Sopenharmony_ci * @param data pointer where to store data
859141cc406Sopenharmony_ci * @param length total bytes to read on one line
860141cc406Sopenharmony_ci * @param ltr total number of lines to read
861141cc406Sopenharmony_ci * @param retry signals that the function must read as much lines it can
862141cc406Sopenharmony_ci * @param x2 tells that lines must be enlarged by a 2 factor
863141cc406Sopenharmony_ci * @param mode COLOR_MODE if color mode
864141cc406Sopenharmony_ci * @returns number of data lines read, -1 in case of error
865141cc406Sopenharmony_ci */
866141cc406Sopenharmony_cistatic int
867141cc406Sopenharmony_ciread_line (P5_Device * dev, uint8_t * data, size_t length, int ltr,
868141cc406Sopenharmony_ci	   SANE_Bool retry, SANE_Bool x2, int mode, SANE_Bool correction)
869141cc406Sopenharmony_ci{
870141cc406Sopenharmony_ci  uint8_t counter, read, cnt;
871141cc406Sopenharmony_ci  uint8_t inbuffer[MAX_SENSOR_PIXELS * 2 * 3 + 2];
872141cc406Sopenharmony_ci  unsigned int i, factor;
873141cc406Sopenharmony_ci  float val;
874141cc406Sopenharmony_ci
875141cc406Sopenharmony_ci  DBG (DBG_proc, "read_line: trying to read %d lines of %lu bytes\n", ltr,
876141cc406Sopenharmony_ci       (unsigned long)length);
877141cc406Sopenharmony_ci
878141cc406Sopenharmony_ci  counter = read_reg (dev->fd, REG9);
879141cc406Sopenharmony_ci  DBG (DBG_io, "read_line: %d bytes available\n", counter * 256);
880141cc406Sopenharmony_ci  read = 0;
881141cc406Sopenharmony_ci  if (x2 == SANE_FALSE)
882141cc406Sopenharmony_ci    {
883141cc406Sopenharmony_ci      factor = 1;
884141cc406Sopenharmony_ci    }
885141cc406Sopenharmony_ci  else
886141cc406Sopenharmony_ci    {
887141cc406Sopenharmony_ci      factor = 2;
888141cc406Sopenharmony_ci    }
889141cc406Sopenharmony_ci
890141cc406Sopenharmony_ci  /* in retry mode we read until not enough data, but in no retry
891141cc406Sopenharmony_ci   * read only one line , counter give us 256 bytes block available
892141cc406Sopenharmony_ci   * and we want an number multiple of color channels */
893141cc406Sopenharmony_ci  cnt = (255 + length / factor) / 256;
894141cc406Sopenharmony_ci  while ((counter > cnt && retry == 1) || (counter > cnt && read == 0))
895141cc406Sopenharmony_ci    {
896141cc406Sopenharmony_ci      /* read data from scanner, first and last byte aren't picture data */
897141cc406Sopenharmony_ci      read_data (dev->fd, inbuffer, length / factor + 2);
898141cc406Sopenharmony_ci
899141cc406Sopenharmony_ci      /* image correction */
900141cc406Sopenharmony_ci      if (correction == SANE_TRUE)
901141cc406Sopenharmony_ci	{
902141cc406Sopenharmony_ci	  for (i = 0; i < length / factor; i++)
903141cc406Sopenharmony_ci	    {
904141cc406Sopenharmony_ci	      val = inbuffer[i + 1] - dev->offset[i];
905141cc406Sopenharmony_ci	      if (val > 0)
906141cc406Sopenharmony_ci		{
907141cc406Sopenharmony_ci		  val = val * dev->gain[i];
908141cc406Sopenharmony_ci		  if (val < 255)
909141cc406Sopenharmony_ci		    inbuffer[i + 1] = val;
910141cc406Sopenharmony_ci		  else
911141cc406Sopenharmony_ci		    inbuffer[i + 1] = 255;
912141cc406Sopenharmony_ci		}
913141cc406Sopenharmony_ci	      else
914141cc406Sopenharmony_ci		{
915141cc406Sopenharmony_ci		  inbuffer[i + 1] = 0;
916141cc406Sopenharmony_ci		}
917141cc406Sopenharmony_ci	    }
918141cc406Sopenharmony_ci	}
919141cc406Sopenharmony_ci
920141cc406Sopenharmony_ci      /* handle horizontal data doubling */
921141cc406Sopenharmony_ci      if (x2 == SANE_FALSE)
922141cc406Sopenharmony_ci	{
923141cc406Sopenharmony_ci	  memcpy (data + read * length, inbuffer + 1, length);
924141cc406Sopenharmony_ci	}
925141cc406Sopenharmony_ci      else
926141cc406Sopenharmony_ci	{
927141cc406Sopenharmony_ci	  if (mode == MODE_COLOR)
928141cc406Sopenharmony_ci	    {
929141cc406Sopenharmony_ci	      for (i = 0; i < length / factor; i += 3)
930141cc406Sopenharmony_ci		{
931141cc406Sopenharmony_ci		  data[read * length + i * factor] = inbuffer[i + 1];
932141cc406Sopenharmony_ci		  data[read * length + i * factor + 1] = inbuffer[i + 2];
933141cc406Sopenharmony_ci		  data[read * length + i * factor + 2] = inbuffer[i + 3];
934141cc406Sopenharmony_ci		  data[read * length + i * factor + 3] = inbuffer[i + 1];
935141cc406Sopenharmony_ci		  data[read * length + i * factor + 4] = inbuffer[i + 2];
936141cc406Sopenharmony_ci		  data[read * length + i * factor + 5] = inbuffer[i + 3];
937141cc406Sopenharmony_ci		}
938141cc406Sopenharmony_ci	    }
939141cc406Sopenharmony_ci	  else
940141cc406Sopenharmony_ci	    {
941141cc406Sopenharmony_ci	      for (i = 0; i < length / factor; i++)
942141cc406Sopenharmony_ci		{
943141cc406Sopenharmony_ci		  data[read * length + i * factor] = inbuffer[i + 1];
944141cc406Sopenharmony_ci		  data[read * length + i * factor + 1] = inbuffer[i + 1];
945141cc406Sopenharmony_ci		}
946141cc406Sopenharmony_ci	    }
947141cc406Sopenharmony_ci	}
948141cc406Sopenharmony_ci      read++;
949141cc406Sopenharmony_ci      if (retry == SANE_TRUE)
950141cc406Sopenharmony_ci	{
951141cc406Sopenharmony_ci	  read_reg (dev->fd, REGF);
952141cc406Sopenharmony_ci	  read_reg (dev->fd, REGA);
953141cc406Sopenharmony_ci	  read_reg (dev->fd, REG9);
954141cc406Sopenharmony_ci	  counter = read_reg (dev->fd, REG9);
955141cc406Sopenharmony_ci	  read_reg (dev->fd, REGA);
956141cc406Sopenharmony_ci	  if (read >= ltr)
957141cc406Sopenharmony_ci	    {
958141cc406Sopenharmony_ci	      DBG (DBG_io, "read_line returning %d lines\n", read);
959141cc406Sopenharmony_ci	      return read;
960141cc406Sopenharmony_ci	    }
961141cc406Sopenharmony_ci	  counter = read_reg (dev->fd, REG9);
962141cc406Sopenharmony_ci	}
963141cc406Sopenharmony_ci    }
964141cc406Sopenharmony_ci  read_reg (dev->fd, REGF);
965141cc406Sopenharmony_ci  read_reg (dev->fd, REGA);
966141cc406Sopenharmony_ci  read_reg (dev->fd, REG9);
967141cc406Sopenharmony_ci  counter = read_reg (dev->fd, REG9);
968141cc406Sopenharmony_ci  read_reg (dev->fd, REGA);
969141cc406Sopenharmony_ci  DBG (DBG_io, "read_line returning %d lines\n", read);
970141cc406Sopenharmony_ci  return read;
971141cc406Sopenharmony_ci}
972141cc406Sopenharmony_ci
973141cc406Sopenharmony_ci
974141cc406Sopenharmony_cistatic SANE_Status
975141cc406Sopenharmony_cieject (int fd)
976141cc406Sopenharmony_ci{
977141cc406Sopenharmony_ci  int detector;
978141cc406Sopenharmony_ci
979141cc406Sopenharmony_ci  DBG (DBG_proc, "eject: start ...\n");
980141cc406Sopenharmony_ci
981141cc406Sopenharmony_ci  do
982141cc406Sopenharmony_ci    {
983141cc406Sopenharmony_ci      write_reg2 (fd, REG1, 0x1110);
984141cc406Sopenharmony_ci      detector = read_reg (fd, REGE);
985141cc406Sopenharmony_ci      detector = read_reg (fd, REGE);
986141cc406Sopenharmony_ci    }
987141cc406Sopenharmony_ci  while ((detector & 0x04) != 0);
988141cc406Sopenharmony_ci  write_reg (fd, REG0, 0x00);
989141cc406Sopenharmony_ci  write_reg (fd, REG1, 0x00);
990141cc406Sopenharmony_ci  write_reg (fd, REGF, 0x82);
991141cc406Sopenharmony_ci  write_reg (fd, REG7, 0x00);
992141cc406Sopenharmony_ci
993141cc406Sopenharmony_ci  DBG (DBG_proc, "eject: end.\n");
994141cc406Sopenharmony_ci  return SANE_STATUS_GOOD;
995141cc406Sopenharmony_ci}
996141cc406Sopenharmony_ci
997141cc406Sopenharmony_ci/** @brief wait for document to be present in feeder
998141cc406Sopenharmony_ci * Polls document sensor until something is present. Give up after 20 seconds
999141cc406Sopenharmony_ci * @param fd file descriptor of the physical device
1000141cc406Sopenharmony_ci */
1001141cc406Sopenharmony_ci/* static int
1002141cc406Sopenharmony_ciwait_document (int fd, uint8_t detector)
1003141cc406Sopenharmony_ci{
1004141cc406Sopenharmony_ci  int count = 0;
1005141cc406Sopenharmony_ci  uint8_t val;
1006141cc406Sopenharmony_ci
1007141cc406Sopenharmony_ci  write_reg (fd, REG1, 0x00);
1008141cc406Sopenharmony_ci  write_reg (fd, REG7, 0x00);
1009141cc406Sopenharmony_ci  detector = read_reg (fd, REGE);
1010141cc406Sopenharmony_ci  while (detector == 0xc3 && count < 20)
1011141cc406Sopenharmony_ci    {
1012141cc406Sopenharmony_ci      sleep (1);
1013141cc406Sopenharmony_ci      count++;
1014141cc406Sopenharmony_ci      detector = read_reg (fd, REGE);
1015141cc406Sopenharmony_ci    }
1016141cc406Sopenharmony_ci  setadresses (fd, 0x002d, 0x09c7);
1017141cc406Sopenharmony_ci  write_reg (fd, REG1, 0x00);
1018141cc406Sopenharmony_ci  write_reg (fd, REG2, 0x90);
1019141cc406Sopenharmony_ci  write_reg (fd, REGF, 0x82);
1020141cc406Sopenharmony_ci  write_reg (fd, REG0, 0x00);
1021141cc406Sopenharmony_ci  val = p5_inb (fd, STATUS) & 0xf8;
1022141cc406Sopenharmony_ci  if (val != 0xf8)
1023141cc406Sopenharmony_ci    {
1024141cc406Sopenharmony_ci      DBG (DBG_error, "wait_document: unexpected STATUS value 0x%02x instead of 0xf8", val);
1025141cc406Sopenharmony_ci    }
1026141cc406Sopenharmony_ci  if (count >= 20)
1027141cc406Sopenharmony_ci    {
1028141cc406Sopenharmony_ci      DBG (DBG_error, "wait_document: failed to detect document!\n");
1029141cc406Sopenharmony_ci      return 0;
1030141cc406Sopenharmony_ci    }
1031141cc406Sopenharmony_ci  return 1;
1032141cc406Sopenharmony_ci} */
1033141cc406Sopenharmony_ci
1034141cc406Sopenharmony_ci/** @brief move at 150 dpi
1035141cc406Sopenharmony_ci * move the paper at 150 dpi motor speed by the amount specified
1036141cc406Sopenharmony_ci * @params dev pointer to the device structure
1037141cc406Sopenharmony_ci */
1038141cc406Sopenharmony_cistatic SANE_Status
1039141cc406Sopenharmony_cimove (P5_Device * dev)
1040141cc406Sopenharmony_ci{
1041141cc406Sopenharmony_ci  int skip, done, read, count;
1042141cc406Sopenharmony_ci  SANE_Status status = SANE_STATUS_GOOD;
1043141cc406Sopenharmony_ci  unsigned char buffer[256];
1044141cc406Sopenharmony_ci
1045141cc406Sopenharmony_ci  DBG (DBG_proc, "move: start\n");
1046141cc406Sopenharmony_ci
1047141cc406Sopenharmony_ci  /* compute number of lines to skip */
1048141cc406Sopenharmony_ci  skip = dev->ystart;
1049141cc406Sopenharmony_ci
1050141cc406Sopenharmony_ci  /* works, but remains to be explained ... */
1051141cc406Sopenharmony_ci  if (dev->ydpi > 300)
1052141cc406Sopenharmony_ci    skip = skip / 2;
1053141cc406Sopenharmony_ci
1054141cc406Sopenharmony_ci  DBG (DBG_io, "move: skipping %d lines at %d dpi\n", skip, dev->ydpi);
1055141cc406Sopenharmony_ci
1056141cc406Sopenharmony_ci  /* we do a real scan of small width, discarding data */
1057141cc406Sopenharmony_ci  done = 0;
1058141cc406Sopenharmony_ci  status = start_scan (dev, MODE_GRAY, dev->ydpi, 0, 256);
1059141cc406Sopenharmony_ci  if (status != SANE_STATUS_GOOD)
1060141cc406Sopenharmony_ci    {
1061141cc406Sopenharmony_ci      DBG (DBG_error, "move: failed to start scan\n");
1062141cc406Sopenharmony_ci      return SANE_STATUS_INVAL;
1063141cc406Sopenharmony_ci    }
1064141cc406Sopenharmony_ci
1065141cc406Sopenharmony_ci  do
1066141cc406Sopenharmony_ci    {
1067141cc406Sopenharmony_ci      /* test if document left the feeder */
1068141cc406Sopenharmony_ci      status = test_document (dev->fd);
1069141cc406Sopenharmony_ci      if (status == SANE_STATUS_NO_DOCS)
1070141cc406Sopenharmony_ci	{
1071141cc406Sopenharmony_ci	  DBG (DBG_info,
1072141cc406Sopenharmony_ci	       "move: document was shorter than the required move\n");
1073141cc406Sopenharmony_ci	  return SANE_STATUS_INVAL;
1074141cc406Sopenharmony_ci	}
1075141cc406Sopenharmony_ci      /* test if data is available */
1076141cc406Sopenharmony_ci      count = available_bytes (dev->fd);
1077141cc406Sopenharmony_ci      if (count)
1078141cc406Sopenharmony_ci	{
1079141cc406Sopenharmony_ci	  read =
1080141cc406Sopenharmony_ci	    read_line (dev, buffer, 256, 1, SANE_FALSE, SANE_FALSE,
1081141cc406Sopenharmony_ci		       MODE_GRAY, SANE_FALSE);
1082141cc406Sopenharmony_ci	  if (read == -1)
1083141cc406Sopenharmony_ci	    {
1084141cc406Sopenharmony_ci	      DBG (DBG_error, "move: failed to read data\n");
1085141cc406Sopenharmony_ci	      return SANE_STATUS_INVAL;
1086141cc406Sopenharmony_ci	    }
1087141cc406Sopenharmony_ci	  done += read;
1088141cc406Sopenharmony_ci	}
1089141cc406Sopenharmony_ci    }
1090141cc406Sopenharmony_ci  while (done < skip);
1091141cc406Sopenharmony_ci
1092141cc406Sopenharmony_ci  /* reset scanner */
1093141cc406Sopenharmony_ci  write_reg2 (dev->fd, REG1, 0x1110);
1094141cc406Sopenharmony_ci  count = read_reg (dev->fd, REGE);
1095141cc406Sopenharmony_ci  count = read_reg (dev->fd, REGE);
1096141cc406Sopenharmony_ci  write_reg (dev->fd, REG0, 0x00);
1097141cc406Sopenharmony_ci  write_reg (dev->fd, REG1, 0x00);
1098141cc406Sopenharmony_ci  write_reg (dev->fd, REGF, 0x82);
1099141cc406Sopenharmony_ci  write_reg (dev->fd, REG7, 0x00);
1100141cc406Sopenharmony_ci
1101141cc406Sopenharmony_ci  DBG (DBG_proc, "move: exit\n");
1102141cc406Sopenharmony_ci  return status;
1103141cc406Sopenharmony_ci}
1104141cc406Sopenharmony_ci
1105141cc406Sopenharmony_ci/** clean up calibration data
1106141cc406Sopenharmony_ci * @param dev device to clean up
1107141cc406Sopenharmony_ci */
1108141cc406Sopenharmony_cistatic void
1109141cc406Sopenharmony_cicleanup_calibration (P5_Device * dev)
1110141cc406Sopenharmony_ci{
1111141cc406Sopenharmony_ci  int i;
1112141cc406Sopenharmony_ci
1113141cc406Sopenharmony_ci  for (i = 0; i < MAX_RESOLUTIONS * 2; i++)
1114141cc406Sopenharmony_ci    {
1115141cc406Sopenharmony_ci      if (dev->calibration_data[i] != NULL)
1116141cc406Sopenharmony_ci	{
1117141cc406Sopenharmony_ci	  free (dev->calibration_data[i]);
1118141cc406Sopenharmony_ci	  dev->calibration_data[i] = NULL;
1119141cc406Sopenharmony_ci	}
1120141cc406Sopenharmony_ci    }
1121141cc406Sopenharmony_ci  dev->calibrated = SANE_FALSE;
1122141cc406Sopenharmony_ci}
1123141cc406Sopenharmony_ci
1124141cc406Sopenharmony_ci/** detect a black scan line
1125141cc406Sopenharmony_ci * parses the given buffer and return SANE_TRUE if the line is an
1126141cc406Sopenharmony_ci * acceptable black line for calibration
1127141cc406Sopenharmony_ci * @param buffer data line to parse
1128141cc406Sopenharmony_ci * @param pixels number of pixels
1129141cc406Sopenharmony_ci * @param mode MODE_COLOR or MODE_GRAY
1130141cc406Sopenharmony_ci * @returns SANE_TRUE if it is considered as a white line
1131141cc406Sopenharmony_ci */
1132141cc406Sopenharmony_cistatic SANE_Bool
1133141cc406Sopenharmony_ciis_black_line (uint8_t * buffer, unsigned int pixels, int mode)
1134141cc406Sopenharmony_ci{
1135141cc406Sopenharmony_ci  unsigned int i, start, end, count, width;
1136141cc406Sopenharmony_ci
1137141cc406Sopenharmony_ci  /* compute width in bytes */
1138141cc406Sopenharmony_ci  if (mode == MODE_COLOR)
1139141cc406Sopenharmony_ci    {
1140141cc406Sopenharmony_ci      width = pixels * 3;
1141141cc406Sopenharmony_ci    }
1142141cc406Sopenharmony_ci  else
1143141cc406Sopenharmony_ci    {
1144141cc406Sopenharmony_ci      width = pixels;
1145141cc406Sopenharmony_ci    }
1146141cc406Sopenharmony_ci
1147141cc406Sopenharmony_ci  /* we allow the calibration target to be narrower than full width, ie
1148141cc406Sopenharmony_ci   * black margin at both ends of the line */
1149141cc406Sopenharmony_ci  start = (5 * width) / 100;
1150141cc406Sopenharmony_ci  end = (95 * width) / 100;
1151141cc406Sopenharmony_ci  count = 0;
1152141cc406Sopenharmony_ci
1153141cc406Sopenharmony_ci  /* count number of black bytes */
1154141cc406Sopenharmony_ci  for (i = start; i < end; i++)
1155141cc406Sopenharmony_ci    {
1156141cc406Sopenharmony_ci      if (buffer[i] > BLACK_LEVEL)
1157141cc406Sopenharmony_ci	{
1158141cc406Sopenharmony_ci	  count++;
1159141cc406Sopenharmony_ci	}
1160141cc406Sopenharmony_ci    }
1161141cc406Sopenharmony_ci
1162141cc406Sopenharmony_ci  /* we allow 3% black pixels maximum */
1163141cc406Sopenharmony_ci  if (count > (3 * width) / 100)
1164141cc406Sopenharmony_ci    {
1165141cc406Sopenharmony_ci      DBG (DBG_io, "is_black_line=SANE_FALSE\n");
1166141cc406Sopenharmony_ci      return SANE_FALSE;
1167141cc406Sopenharmony_ci    }
1168141cc406Sopenharmony_ci
1169141cc406Sopenharmony_ci  DBG (DBG_io, "is_black_line=SANE_TRUE\n");
1170141cc406Sopenharmony_ci  return SANE_TRUE;
1171141cc406Sopenharmony_ci}
1172141cc406Sopenharmony_ci
1173141cc406Sopenharmony_ci/** detect a white scan line
1174141cc406Sopenharmony_ci * parses the given buffer and return SANE_TRUE if the line is an
1175141cc406Sopenharmony_ci * acceptable white line for calibration
1176141cc406Sopenharmony_ci * @param buffer data line to parse
1177141cc406Sopenharmony_ci * @param pixels number of pixels
1178141cc406Sopenharmony_ci * @param mode MODE_COLOR or MODE_GRAY
1179141cc406Sopenharmony_ci * @returns SANE_TRUE if it is considered as a white line
1180141cc406Sopenharmony_ci */
1181141cc406Sopenharmony_cistatic SANE_Bool
1182141cc406Sopenharmony_ciis_white_line (uint8_t * buffer, unsigned int pixels, int mode)
1183141cc406Sopenharmony_ci{
1184141cc406Sopenharmony_ci  unsigned int i, start, end, count, width;
1185141cc406Sopenharmony_ci
1186141cc406Sopenharmony_ci  /* compute width in bytes */
1187141cc406Sopenharmony_ci  if (mode == MODE_COLOR)
1188141cc406Sopenharmony_ci    {
1189141cc406Sopenharmony_ci      width = pixels * 3;
1190141cc406Sopenharmony_ci    }
1191141cc406Sopenharmony_ci  else
1192141cc406Sopenharmony_ci    {
1193141cc406Sopenharmony_ci      width = pixels;
1194141cc406Sopenharmony_ci    }
1195141cc406Sopenharmony_ci
1196141cc406Sopenharmony_ci  /* we allow the calibration target to be narrower than full width, ie
1197141cc406Sopenharmony_ci   * black margin at both ends of the line */
1198141cc406Sopenharmony_ci  start = (5 * width) / 100;
1199141cc406Sopenharmony_ci  end = (95 * width) / 100;
1200141cc406Sopenharmony_ci  count = 0;
1201141cc406Sopenharmony_ci
1202141cc406Sopenharmony_ci  /* count number of black bytes */
1203141cc406Sopenharmony_ci  for (i = start; i < end; i++)
1204141cc406Sopenharmony_ci    {
1205141cc406Sopenharmony_ci      if (buffer[i] < BLACK_LEVEL)
1206141cc406Sopenharmony_ci	{
1207141cc406Sopenharmony_ci	  count++;
1208141cc406Sopenharmony_ci	}
1209141cc406Sopenharmony_ci    }
1210141cc406Sopenharmony_ci
1211141cc406Sopenharmony_ci  /* we allow 3% black pixels maximum */
1212141cc406Sopenharmony_ci  if (count > (3 * width) / 100)
1213141cc406Sopenharmony_ci    {
1214141cc406Sopenharmony_ci      DBG (DBG_io, "is_white_line=SANE_FALSE\n");
1215141cc406Sopenharmony_ci      return SANE_FALSE;
1216141cc406Sopenharmony_ci    }
1217141cc406Sopenharmony_ci
1218141cc406Sopenharmony_ci  DBG (DBG_io, "is_white_line=SANE_TRUE\n");
1219141cc406Sopenharmony_ci  return SANE_TRUE;
1220141cc406Sopenharmony_ci}
1221141cc406Sopenharmony_ci
1222141cc406Sopenharmony_ci/* ------------------------------------------------------------------------- */
1223141cc406Sopenharmony_ci/* writes gray data to a pnm file */
1224141cc406Sopenharmony_ci/*
1225141cc406Sopenharmony_cistatic void
1226141cc406Sopenharmony_ciwrite_gray_data (unsigned char *image, char *name, SANE_Int width,
1227141cc406Sopenharmony_ci		 SANE_Int height)
1228141cc406Sopenharmony_ci{
1229141cc406Sopenharmony_ci  FILE *fdbg = NULL;
1230141cc406Sopenharmony_ci
1231141cc406Sopenharmony_ci  fdbg = fopen (name, "wb");
1232141cc406Sopenharmony_ci  if (fdbg == NULL)
1233141cc406Sopenharmony_ci    return;
1234141cc406Sopenharmony_ci  fprintf (fdbg, "P5\n%d %d\n255\n", width, height);
1235141cc406Sopenharmony_ci  fwrite (image, width, height, fdbg);
1236141cc406Sopenharmony_ci  fclose (fdbg);
1237141cc406Sopenharmony_ci}
1238141cc406Sopenharmony_ci*/
1239141cc406Sopenharmony_ci
1240141cc406Sopenharmony_ci/* ------------------------------------------------------------------------- */
1241141cc406Sopenharmony_ci/* writes rgb data to a pnm file */
1242141cc406Sopenharmony_cistatic void
1243141cc406Sopenharmony_ciwrite_rgb_data (char *name, unsigned char *image, SANE_Int width,
1244141cc406Sopenharmony_ci		SANE_Int height)
1245141cc406Sopenharmony_ci{
1246141cc406Sopenharmony_ci  FILE *fdbg = NULL;
1247141cc406Sopenharmony_ci
1248141cc406Sopenharmony_ci  fdbg = fopen (name, "wb");
1249141cc406Sopenharmony_ci  if (fdbg == NULL)
1250141cc406Sopenharmony_ci    return;
1251141cc406Sopenharmony_ci  fprintf (fdbg, "P6\n%d %d\n255\n", width, height);
1252141cc406Sopenharmony_ci  fwrite (image, width * 3, height, fdbg);
1253141cc406Sopenharmony_ci  fclose (fdbg);
1254141cc406Sopenharmony_ci}
1255141cc406Sopenharmony_ci
1256141cc406Sopenharmony_ci/** give calibration file name
1257141cc406Sopenharmony_ci * computes the calibration file name to use based on the
1258141cc406Sopenharmony_ci * backend name and device
1259141cc406Sopenharmony_ci */
1260141cc406Sopenharmony_cistatic char *
1261141cc406Sopenharmony_cicalibration_file (const char *devicename)
1262141cc406Sopenharmony_ci{
1263141cc406Sopenharmony_ci  char *ptr = NULL;
1264141cc406Sopenharmony_ci  char tmp_str[PATH_MAX];
1265141cc406Sopenharmony_ci
1266141cc406Sopenharmony_ci  ptr = getenv ("HOME");
1267141cc406Sopenharmony_ci  if (ptr != NULL)
1268141cc406Sopenharmony_ci    {
1269141cc406Sopenharmony_ci      sprintf (tmp_str, "%s/.sane/p5-%s.cal", ptr, devicename);
1270141cc406Sopenharmony_ci    }
1271141cc406Sopenharmony_ci  else
1272141cc406Sopenharmony_ci    {
1273141cc406Sopenharmony_ci      ptr = getenv ("TMPDIR");
1274141cc406Sopenharmony_ci      if (ptr != NULL)
1275141cc406Sopenharmony_ci	{
1276141cc406Sopenharmony_ci	  sprintf (tmp_str, "%s/p5-%s.cal", ptr, devicename);
1277141cc406Sopenharmony_ci	}
1278141cc406Sopenharmony_ci      else
1279141cc406Sopenharmony_ci	{
1280141cc406Sopenharmony_ci	  sprintf (tmp_str, "/tmp/p5-%s.cal", devicename);
1281141cc406Sopenharmony_ci	}
1282141cc406Sopenharmony_ci    }
1283141cc406Sopenharmony_ci  DBG (DBG_trace, "calibration_file: using >%s< for calibration file name\n",
1284141cc406Sopenharmony_ci       tmp_str);
1285141cc406Sopenharmony_ci  return strdup (tmp_str);
1286141cc406Sopenharmony_ci}
1287141cc406Sopenharmony_ci
1288141cc406Sopenharmony_ci/** restore calibration data
1289141cc406Sopenharmony_ci * restore calibration data by loading previously saved calibration data
1290141cc406Sopenharmony_ci * @param dev device to restore
1291141cc406Sopenharmony_ci * @return SANE_STATUS_GOOD on success, otherwise error code
1292141cc406Sopenharmony_ci */
1293141cc406Sopenharmony_cistatic SANE_Status
1294141cc406Sopenharmony_cirestore_calibration (P5_Device * dev)
1295141cc406Sopenharmony_ci{
1296141cc406Sopenharmony_ci  SANE_Status status = SANE_STATUS_GOOD;
1297141cc406Sopenharmony_ci  char *fname = NULL;
1298141cc406Sopenharmony_ci  FILE *fcalib = NULL;
1299141cc406Sopenharmony_ci  size_t size;
1300141cc406Sopenharmony_ci  int i;
1301141cc406Sopenharmony_ci
1302141cc406Sopenharmony_ci  DBG (DBG_proc, "restore_calibration: start\n");
1303141cc406Sopenharmony_ci  cleanup_calibration (dev);
1304141cc406Sopenharmony_ci  fname = calibration_file (dev->model->name);
1305141cc406Sopenharmony_ci  fcalib = fopen (fname, "rb");
1306141cc406Sopenharmony_ci  if (fcalib == NULL)
1307141cc406Sopenharmony_ci    {
1308141cc406Sopenharmony_ci      DBG (DBG_error, "restore_calibration: failed to open %s!\n", fname);
1309141cc406Sopenharmony_ci      free (fname);
1310141cc406Sopenharmony_ci      return SANE_STATUS_IO_ERROR;
1311141cc406Sopenharmony_ci    }
1312141cc406Sopenharmony_ci
1313141cc406Sopenharmony_ci  /* loop filling calibration data until EOF reached */
1314141cc406Sopenharmony_ci  i = 0;
1315141cc406Sopenharmony_ci  while (!feof (fcalib) && (i < 2 * MAX_RESOLUTIONS))
1316141cc406Sopenharmony_ci    {
1317141cc406Sopenharmony_ci      dev->calibration_data[i] = malloc (sizeof (P5_Calibration_Data));
1318141cc406Sopenharmony_ci      if (dev->calibration_data[i] == NULL)
1319141cc406Sopenharmony_ci	{
1320141cc406Sopenharmony_ci	  cleanup_calibration (dev);
1321141cc406Sopenharmony_ci	  free (fname);
1322141cc406Sopenharmony_ci	  fclose (fcalib);
1323141cc406Sopenharmony_ci	  DBG (DBG_error,
1324141cc406Sopenharmony_ci	       "restore_calibration: failed to allocate memory for calibration\n");
1325141cc406Sopenharmony_ci	  return SANE_STATUS_NO_MEM;
1326141cc406Sopenharmony_ci	}
1327141cc406Sopenharmony_ci      size =
1328141cc406Sopenharmony_ci	fread (dev->calibration_data[i], 1, sizeof (P5_Calibration_Data),
1329141cc406Sopenharmony_ci	       fcalib);
1330141cc406Sopenharmony_ci      if (feof (fcalib))
1331141cc406Sopenharmony_ci	{
1332141cc406Sopenharmony_ci	  free (dev->calibration_data[i]);
1333141cc406Sopenharmony_ci	  dev->calibration_data[i] = NULL;
1334141cc406Sopenharmony_ci	}
1335141cc406Sopenharmony_ci      else if (size != sizeof (P5_Calibration_Data))
1336141cc406Sopenharmony_ci	{
1337141cc406Sopenharmony_ci	  cleanup_calibration (dev);
1338141cc406Sopenharmony_ci	  free (fname);
1339141cc406Sopenharmony_ci	  fclose (fcalib);
1340141cc406Sopenharmony_ci	  DBG (DBG_error, "restore_calibration: failed to read from file\n");
1341141cc406Sopenharmony_ci	  return SANE_STATUS_IO_ERROR;
1342141cc406Sopenharmony_ci	}
1343141cc406Sopenharmony_ci      DBG (DBG_trace,
1344141cc406Sopenharmony_ci	   "restore_calibration: read 1 calibration structure from file\n");
1345141cc406Sopenharmony_ci      i++;
1346141cc406Sopenharmony_ci    }
1347141cc406Sopenharmony_ci
1348141cc406Sopenharmony_ci  dev->calibrated = SANE_TRUE;
1349141cc406Sopenharmony_ci  fclose (fcalib);
1350141cc406Sopenharmony_ci  free (fname);
1351141cc406Sopenharmony_ci
1352141cc406Sopenharmony_ci  DBG (DBG_proc, "restore_calibration: end\n");
1353141cc406Sopenharmony_ci  return status;
1354141cc406Sopenharmony_ci}
1355141cc406Sopenharmony_ci
1356141cc406Sopenharmony_ci/** save calibration data
1357141cc406Sopenharmony_ci * save calibration data from memory to file
1358141cc406Sopenharmony_ci * @param dev device calibration to save
1359141cc406Sopenharmony_ci * @return SANE_STATUS_GOOD on success, otherwise error code
1360141cc406Sopenharmony_ci */
1361141cc406Sopenharmony_cistatic SANE_Status
1362141cc406Sopenharmony_cisave_calibration (P5_Device * dev)
1363141cc406Sopenharmony_ci{
1364141cc406Sopenharmony_ci  SANE_Status status = SANE_STATUS_GOOD;
1365141cc406Sopenharmony_ci  char *fname = NULL;
1366141cc406Sopenharmony_ci  FILE *fcalib = NULL;
1367141cc406Sopenharmony_ci  int i;
1368141cc406Sopenharmony_ci  size_t size;
1369141cc406Sopenharmony_ci
1370141cc406Sopenharmony_ci  DBG (DBG_proc, "save_calibration: start\n");
1371141cc406Sopenharmony_ci  fname = calibration_file (dev->model->name);
1372141cc406Sopenharmony_ci  fcalib = fopen (fname, "wb");
1373141cc406Sopenharmony_ci  if (fcalib == NULL)
1374141cc406Sopenharmony_ci    {
1375141cc406Sopenharmony_ci      DBG (DBG_error, "save_calibration: failed to open %s!\n", fname);
1376141cc406Sopenharmony_ci      free (fname);
1377141cc406Sopenharmony_ci      return SANE_STATUS_IO_ERROR;
1378141cc406Sopenharmony_ci    }
1379141cc406Sopenharmony_ci
1380141cc406Sopenharmony_ci  /* loop filling calibration data until EOF reached */
1381141cc406Sopenharmony_ci  i = 0;
1382141cc406Sopenharmony_ci  while (dev->calibration_data[i] != NULL && (i < 2 * MAX_RESOLUTIONS))
1383141cc406Sopenharmony_ci    {
1384141cc406Sopenharmony_ci      size =
1385141cc406Sopenharmony_ci	fwrite (dev->calibration_data[i], sizeof (P5_Calibration_Data), 1,
1386141cc406Sopenharmony_ci		fcalib);
1387141cc406Sopenharmony_ci      if (size != sizeof (P5_Calibration_Data))
1388141cc406Sopenharmony_ci	{
1389141cc406Sopenharmony_ci	  free (fname);
1390141cc406Sopenharmony_ci	  fclose (fcalib);
1391141cc406Sopenharmony_ci	  DBG (DBG_error, "save_calibration: failed to write to file\n");
1392141cc406Sopenharmony_ci	  return SANE_STATUS_IO_ERROR;
1393141cc406Sopenharmony_ci	}
1394141cc406Sopenharmony_ci      DBG (DBG_trace,
1395141cc406Sopenharmony_ci	   "save_calibration: wrote 1 calibration structure to file\n");
1396141cc406Sopenharmony_ci      i++;
1397141cc406Sopenharmony_ci    }
1398141cc406Sopenharmony_ci
1399141cc406Sopenharmony_ci  fclose (fcalib);
1400141cc406Sopenharmony_ci  free (fname);
1401141cc406Sopenharmony_ci
1402141cc406Sopenharmony_ci  DBG (DBG_proc, "save_calibration: end\n");
1403141cc406Sopenharmony_ci  return status;
1404141cc406Sopenharmony_ci}
1405141cc406Sopenharmony_ci
1406141cc406Sopenharmony_ci/** calibrate scanner
1407141cc406Sopenharmony_ci * calibrates scanner by scanning a white sheet to get
1408141cc406Sopenharmony_ci * reference data. The black reference data is extracted from the lines
1409141cc406Sopenharmony_ci * that precede the physical document.
1410141cc406Sopenharmony_ci * Calibration is done at 300 color, then data is built for other modes
1411141cc406Sopenharmony_ci * and resolutions.
1412141cc406Sopenharmony_ci * @param dev device to calibrate
1413141cc406Sopenharmony_ci */
1414141cc406Sopenharmony_cistatic SANE_Status
1415141cc406Sopenharmony_cisheetfed_calibration (P5_Device * dev)
1416141cc406Sopenharmony_ci{
1417141cc406Sopenharmony_ci  uint8_t buffer[MAX_SENSOR_PIXELS * 3];
1418141cc406Sopenharmony_ci  uint16_t white_data[MAX_SENSOR_PIXELS * 3];
1419141cc406Sopenharmony_ci  uint16_t black_data[MAX_SENSOR_PIXELS * 3];
1420141cc406Sopenharmony_ci  unsigned int i, j, k, dpi, pixels, read, black, white;
1421141cc406Sopenharmony_ci  float coeff;
1422141cc406Sopenharmony_ci  unsigned int red, green, blue;
1423141cc406Sopenharmony_ci  int line;
1424141cc406Sopenharmony_ci  SANE_Status status;
1425141cc406Sopenharmony_ci  char title[40];
1426141cc406Sopenharmony_ci
1427141cc406Sopenharmony_ci  FILE *dbg = fopen ("debug.pnm", "wb");
1428141cc406Sopenharmony_ci  fprintf (dbg, "P6\n%d %d\n255\n", MAX_SENSOR_PIXELS,
1429141cc406Sopenharmony_ci	   CALIBRATION_SKIP_LINES * 4);
1430141cc406Sopenharmony_ci
1431141cc406Sopenharmony_ci  DBG (DBG_proc, "sheetfed_calibration: start\n");
1432141cc406Sopenharmony_ci
1433141cc406Sopenharmony_ci  /* check calibration target has been loaded in ADF */
1434141cc406Sopenharmony_ci  status = test_document (dev->fd);
1435141cc406Sopenharmony_ci  if (status == SANE_STATUS_NO_DOCS)
1436141cc406Sopenharmony_ci    {
1437141cc406Sopenharmony_ci      DBG (DBG_error,
1438141cc406Sopenharmony_ci	   "sheetfed_calibration: no calibration target present!\n");
1439141cc406Sopenharmony_ci      return SANE_STATUS_NO_DOCS;
1440141cc406Sopenharmony_ci    }
1441141cc406Sopenharmony_ci
1442141cc406Sopenharmony_ci  /* clean up calibration data */
1443141cc406Sopenharmony_ci  cleanup_calibration (dev);
1444141cc406Sopenharmony_ci
1445141cc406Sopenharmony_ci  /*  a RGB scan to get reference data */
1446141cc406Sopenharmony_ci  /* initialize calibration slot for the resolution */
1447141cc406Sopenharmony_ci  i = 0;
1448141cc406Sopenharmony_ci  dpi = dev->model->max_xdpi;
1449141cc406Sopenharmony_ci  pixels = MAX_SENSOR_PIXELS;
1450141cc406Sopenharmony_ci  dev->calibration_data[i] =
1451141cc406Sopenharmony_ci    (P5_Calibration_Data *) malloc (sizeof (P5_Calibration_Data));
1452141cc406Sopenharmony_ci  if (dev->calibration_data[i] == NULL)
1453141cc406Sopenharmony_ci    {
1454141cc406Sopenharmony_ci      cleanup_calibration (dev);
1455141cc406Sopenharmony_ci      DBG (DBG_error,
1456141cc406Sopenharmony_ci	   "sheetfed_calibration: failed to allocate memory for calibration\n");
1457141cc406Sopenharmony_ci      return SANE_STATUS_NO_MEM;
1458141cc406Sopenharmony_ci    }
1459141cc406Sopenharmony_ci  dev->calibration_data[i]->dpi = dpi;
1460141cc406Sopenharmony_ci
1461141cc406Sopenharmony_ci  /* start scan */
1462141cc406Sopenharmony_ci  status = start_scan (dev, MODE_COLOR, dpi, 0, pixels);
1463141cc406Sopenharmony_ci  if (status != SANE_STATUS_GOOD)
1464141cc406Sopenharmony_ci    {
1465141cc406Sopenharmony_ci      cleanup_calibration (dev);
1466141cc406Sopenharmony_ci      DBG (DBG_error,
1467141cc406Sopenharmony_ci	   "sheetfed_calibration: failed to start scan at %d dpi\n", dpi);
1468141cc406Sopenharmony_ci      return SANE_STATUS_INVAL;
1469141cc406Sopenharmony_ci    }
1470141cc406Sopenharmony_ci
1471141cc406Sopenharmony_ci  white = 0;
1472141cc406Sopenharmony_ci  black = 0;
1473141cc406Sopenharmony_ci  read = 0;
1474141cc406Sopenharmony_ci  for (j = 0; j < pixels * 3; j++)
1475141cc406Sopenharmony_ci    {
1476141cc406Sopenharmony_ci      black_data[j] = 0;
1477141cc406Sopenharmony_ci      white_data[j] = 0;
1478141cc406Sopenharmony_ci    }
1479141cc406Sopenharmony_ci
1480141cc406Sopenharmony_ci  /* read lines and gather black and white ones until enough for sensor's
1481141cc406Sopenharmony_ci   * native resolution */
1482141cc406Sopenharmony_ci  do
1483141cc406Sopenharmony_ci    {
1484141cc406Sopenharmony_ci      status = test_document (dev->fd);
1485141cc406Sopenharmony_ci      if (status == SANE_STATUS_NO_DOCS && (white < 10 || black < 10))
1486141cc406Sopenharmony_ci	{
1487141cc406Sopenharmony_ci	  cleanup_calibration (dev);
1488141cc406Sopenharmony_ci	  DBG (DBG_error,
1489141cc406Sopenharmony_ci	       "sheetfed_calibration: calibration sheet too short!\n");
1490141cc406Sopenharmony_ci	  return SANE_STATUS_INVAL;
1491141cc406Sopenharmony_ci	}
1492141cc406Sopenharmony_ci      memset (buffer, 0x00, MAX_SENSOR_PIXELS * 3);
1493141cc406Sopenharmony_ci      line =
1494141cc406Sopenharmony_ci	read_line (dev, buffer, pixels * 3, 1, SANE_FALSE, SANE_FALSE,
1495141cc406Sopenharmony_ci		   MODE_COLOR, SANE_FALSE);
1496141cc406Sopenharmony_ci      if (line == -1)
1497141cc406Sopenharmony_ci	{
1498141cc406Sopenharmony_ci	  DBG (DBG_error, "sheetfed_calibration: failed to read data\n");
1499141cc406Sopenharmony_ci	  return SANE_STATUS_INVAL;
1500141cc406Sopenharmony_ci	}
1501141cc406Sopenharmony_ci
1502141cc406Sopenharmony_ci      /* if a data line has been read, add it to reference data */
1503141cc406Sopenharmony_ci      if (line)
1504141cc406Sopenharmony_ci	{
1505141cc406Sopenharmony_ci	  read++;
1506141cc406Sopenharmony_ci	  fwrite (buffer, pixels * 3, 1, dbg);
1507141cc406Sopenharmony_ci	  if (is_white_line (buffer, pixels, MODE_COLOR) && white < 256)
1508141cc406Sopenharmony_ci	    {
1509141cc406Sopenharmony_ci	      white++;
1510141cc406Sopenharmony_ci	      /* first calibration lines are skipped */
1511141cc406Sopenharmony_ci	      for (j = 0; j < pixels * 3 && read > CALIBRATION_SKIP_LINES;
1512141cc406Sopenharmony_ci		   j++)
1513141cc406Sopenharmony_ci		{
1514141cc406Sopenharmony_ci		  white_data[j] += buffer[j];
1515141cc406Sopenharmony_ci		}
1516141cc406Sopenharmony_ci	    }
1517141cc406Sopenharmony_ci	  if (is_black_line (buffer, pixels, MODE_COLOR) && black < 256)
1518141cc406Sopenharmony_ci	    {
1519141cc406Sopenharmony_ci	      black++;
1520141cc406Sopenharmony_ci	      for (j = 0; j < pixels * 3; j++)
1521141cc406Sopenharmony_ci		{
1522141cc406Sopenharmony_ci		  black_data[j] += buffer[j];
1523141cc406Sopenharmony_ci		}
1524141cc406Sopenharmony_ci	    }
1525141cc406Sopenharmony_ci	}
1526141cc406Sopenharmony_ci    }
1527141cc406Sopenharmony_ci  while (test_document (dev->fd) != SANE_STATUS_NO_DOCS);
1528141cc406Sopenharmony_ci  DBG (DBG_trace, "sheetfed_calibration: white lines=%d, black lines=%d\n",
1529141cc406Sopenharmony_ci       white, black);
1530141cc406Sopenharmony_ci
1531141cc406Sopenharmony_ci  /* average pixels and store in per dpi calibration data */
1532141cc406Sopenharmony_ci  for (j = 0; j < pixels * 3; j++)
1533141cc406Sopenharmony_ci    {
1534141cc406Sopenharmony_ci      dev->calibration_data[i]->white_data[j] = white_data[j] / white;
1535141cc406Sopenharmony_ci      dev->calibration_data[i]->black_data[j] = black_data[j] / black;
1536141cc406Sopenharmony_ci    }
1537141cc406Sopenharmony_ci
1538141cc406Sopenharmony_ci  /* we average red, green and blue offset on the full sensor */
1539141cc406Sopenharmony_ci  red = 0;
1540141cc406Sopenharmony_ci  green = 0;
1541141cc406Sopenharmony_ci  blue = 0;
1542141cc406Sopenharmony_ci  for (j = 0; j < pixels * 3; j += 3)
1543141cc406Sopenharmony_ci    {
1544141cc406Sopenharmony_ci      red += dev->calibration_data[i]->black_data[j];
1545141cc406Sopenharmony_ci      green += dev->calibration_data[i]->black_data[j + 1];
1546141cc406Sopenharmony_ci      blue += dev->calibration_data[i]->black_data[j + 2];
1547141cc406Sopenharmony_ci    }
1548141cc406Sopenharmony_ci  for (j = 0; j < pixels * 3; j += 3)
1549141cc406Sopenharmony_ci    {
1550141cc406Sopenharmony_ci      dev->calibration_data[i]->black_data[j] = red / pixels;
1551141cc406Sopenharmony_ci      dev->calibration_data[i]->black_data[j + 1] = green / pixels;
1552141cc406Sopenharmony_ci      dev->calibration_data[i]->black_data[j + 2] = blue / pixels;
1553141cc406Sopenharmony_ci    }
1554141cc406Sopenharmony_ci
1555141cc406Sopenharmony_ci  /* trace calibration data for debug */
1556141cc406Sopenharmony_ci  if (DBG_LEVEL > DBG_data)
1557141cc406Sopenharmony_ci    {
1558141cc406Sopenharmony_ci      sprintf (title, "calibration-white-%d.pnm",
1559141cc406Sopenharmony_ci	       dev->calibration_data[i]->dpi);
1560141cc406Sopenharmony_ci      write_rgb_data (title, dev->calibration_data[i]->white_data, pixels, 1);
1561141cc406Sopenharmony_ci      sprintf (title, "calibration-black-%d.pnm",
1562141cc406Sopenharmony_ci	       dev->calibration_data[i]->dpi);
1563141cc406Sopenharmony_ci      write_rgb_data (title, dev->calibration_data[i]->black_data, pixels, 1);
1564141cc406Sopenharmony_ci    }
1565141cc406Sopenharmony_ci
1566141cc406Sopenharmony_ci  /* loop on all remaining resolution and compute calibration data from it */
1567141cc406Sopenharmony_ci  for (i = 1; i < MAX_RESOLUTIONS && dev->model->xdpi_values[i] > 0; i++)
1568141cc406Sopenharmony_ci    {
1569141cc406Sopenharmony_ci      dev->calibration_data[i] =
1570141cc406Sopenharmony_ci	(P5_Calibration_Data *) malloc (sizeof (P5_Calibration_Data));
1571141cc406Sopenharmony_ci      if (dev->calibration_data[i] == NULL)
1572141cc406Sopenharmony_ci	{
1573141cc406Sopenharmony_ci	  cleanup_calibration (dev);
1574141cc406Sopenharmony_ci	  DBG (DBG_error,
1575141cc406Sopenharmony_ci	       "sheetfed_calibration: failed to allocate memory for calibration\n");
1576141cc406Sopenharmony_ci	  return SANE_STATUS_INVAL;
1577141cc406Sopenharmony_ci	}
1578141cc406Sopenharmony_ci      dev->calibration_data[i]->dpi = dev->model->xdpi_values[i];
1579141cc406Sopenharmony_ci
1580141cc406Sopenharmony_ci      coeff = ((float) dev->model->xdpi_values[i]) / (float) dpi;
1581141cc406Sopenharmony_ci
1582141cc406Sopenharmony_ci      /* generate data by decimation */
1583141cc406Sopenharmony_ci      for (j = 0; j < pixels / coeff; j++)
1584141cc406Sopenharmony_ci	{
1585141cc406Sopenharmony_ci	  k = j * coeff;
1586141cc406Sopenharmony_ci	  dev->calibration_data[i]->white_data[j] =
1587141cc406Sopenharmony_ci	    dev->calibration_data[0]->white_data[k];
1588141cc406Sopenharmony_ci	  dev->calibration_data[i]->white_data[j + 1] =
1589141cc406Sopenharmony_ci	    dev->calibration_data[0]->white_data[k + 1];
1590141cc406Sopenharmony_ci	  dev->calibration_data[i]->white_data[j + 2] =
1591141cc406Sopenharmony_ci	    dev->calibration_data[0]->white_data[k + 2];
1592141cc406Sopenharmony_ci	  dev->calibration_data[i]->black_data[j] =
1593141cc406Sopenharmony_ci	    dev->calibration_data[0]->black_data[k];
1594141cc406Sopenharmony_ci	  dev->calibration_data[i]->black_data[j + 1] =
1595141cc406Sopenharmony_ci	    dev->calibration_data[0]->black_data[k + 1];
1596141cc406Sopenharmony_ci	  dev->calibration_data[i]->black_data[j + 2] =
1597141cc406Sopenharmony_ci	    dev->calibration_data[0]->black_data[k + 2];
1598141cc406Sopenharmony_ci	}
1599141cc406Sopenharmony_ci    }
1600141cc406Sopenharmony_ci
1601141cc406Sopenharmony_ci  fclose (dbg);
1602141cc406Sopenharmony_ci  dev->calibrated = SANE_TRUE;
1603141cc406Sopenharmony_ci
1604141cc406Sopenharmony_ci  /* eject calibration target */
1605141cc406Sopenharmony_ci  eject (dev->fd);
1606141cc406Sopenharmony_ci
1607141cc406Sopenharmony_ci  DBG (DBG_proc, "sheetfed_calibration: end\n");
1608141cc406Sopenharmony_ci  return SANE_STATUS_GOOD;
1609141cc406Sopenharmony_ci}
1610141cc406Sopenharmony_ci
1611141cc406Sopenharmony_ci/* vim: set sw=2 cino=>2se-1sn-1s{s^-1st0(0u0 smarttab expandtab: */
1612