1 /* $NetBSD: ss_scanjet.c,v 1.28.8.1 2004/09/11 12:57:39 he Exp $ */
2
3 /*
4 * Copyright (c) 1995 Kenneth Stailey. All rights reserved.
5 *
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
8 * are met:
9 * 1. Redistributions of source code must retain the above copyright
10 * notice, this list of conditions and the following disclaimer.
11 * 2. Redistributions in binary form must reproduce the above copyright
12 * notice, this list of conditions and the following disclaimer in the
13 * documentation and/or other materials provided with the distribution.
14 * 3. All advertising materials mentioning features or use of this software
15 * must display the following acknowledgement:
16 * This product includes software developed by Kenneth Stailey.
17 * 4. The name of the author may not be used to endorse or promote products
18 * derived from this software without specific prior written permission.
19 *
20 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
21 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
22 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
23 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
24 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
25 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
26 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
27 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
28 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
29 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
30 */
31
32 /*
33 * special functions for the HP ScanJet IIc and IIcx
34 */
35
36 #include <sys/cdefs.h>
37 __KERNEL_RCSID(0, "$NetBSD: ss_scanjet.c,v 1.28.8.1 2004/09/11 12:57:39 he Exp $");
38
39 #include <sys/param.h>
40 #include <sys/systm.h>
41 #include <sys/fcntl.h>
42 #include <sys/errno.h>
43 #include <sys/ioctl.h>
44 #include <sys/malloc.h>
45 #include <sys/buf.h>
46 #include <sys/proc.h>
47 #include <sys/user.h>
48 #include <sys/device.h>
49 #include <sys/conf.h> /* for cdevsw */
50 #include <sys/scanio.h>
51 #include <sys/kernel.h>
52
53 #include <dev/scsipi/scsi_all.h>
54 #include <dev/scsipi/scsipi_all.h>
55 #include <dev/scsipi/scsi_scanner.h>
56 #include <dev/scsipi/scsiconf.h>
57 #include <dev/scsipi/scsipi_base.h>
58 #include <dev/scsipi/ssvar.h>
59
60 #define SCANJET_RETRIES 4
61
62 int scanjet_get_params __P((struct ss_softc *));
63 int scanjet_set_params __P((struct ss_softc *, struct scan_io *));
64 int scanjet_trigger_scanner __P((struct ss_softc *));
65 int scanjet_read __P((struct ss_softc *, struct buf *));
66
67 /* only used internally */
68 int scanjet_ctl_write __P((struct ss_softc *, char *, u_int));
69 int scanjet_ctl_read __P((struct ss_softc *, char *, u_int));
70 int scanjet_set_window __P((struct ss_softc *));
71 int scanjet_compute_sizes __P((struct ss_softc *));
72
73 /*
74 * structure for the special handlers
75 */
76 struct ss_special scanjet_special = {
77 scanjet_set_params,
78 scanjet_trigger_scanner,
79 scanjet_get_params,
80 NULL, /* no special minphys */
81 scanjet_read, /* scsi 6-byte read */
82 NULL, /* no "rewind" code (yet?) */
83 NULL, /* no adf support right now */
84 NULL /* no adf support right now */
85 };
86
87 /*
88 * scanjet_attach: attach special functions to ss
89 */
90 void
91 scanjet_attach(ss, sa)
92 struct ss_softc *ss;
93 struct scsipibus_attach_args *sa;
94 {
95 int error;
96
97 SC_DEBUG(ss->sc_periph, SCSIPI_DB1, ("scanjet_attach: start\n"));
98 ss->sio.scan_scanner_type = 0;
99
100 printf("%s: ", ss->sc_dev.dv_xname);
101
102 /* first, check the model (which determines nothing yet) */
103
104 if (!memcmp(sa->sa_inqbuf.product, "C1750A", 6)) {
105 ss->sio.scan_scanner_type = HP_SCANJET_IIC;
106 printf("HP ScanJet IIc");
107 } else if (!memcmp(sa->sa_inqbuf.product, "C2500A", 6)) {
108 ss->sio.scan_scanner_type = HP_SCANJET_IIC;
109 printf("HP ScanJet IIcx");
110 } else if (!memcmp(sa->sa_inqbuf.product, "C2520A", 6)) {
111 ss->sio.scan_scanner_type = HP_SCANJET_IIC;
112 printf("HP ScanJet 4c");
113 } else if (!memcmp(sa->sa_inqbuf.product, "C1130A", 6)) {
114 ss->sio.scan_scanner_type = HP_SCANJET_IIC;
115 printf("HP ScanJet 4p");
116 } else if (!memcmp(sa->sa_inqbuf.product, "C5110A", 6)) {
117 ss->sio.scan_scanner_type = HP_SCANJET_IIC;
118 printf("HP ScanJet 5p");
119 } else {
120 ss->sio.scan_scanner_type = HP_SCANJET_IIC;
121 printf("HP ScanJet (unknown model)");
122 }
123
124 SC_DEBUG(ss->sc_periph, SCSIPI_DB1,
125 ("scanjet_attach: scanner_type = %d\n",
126 ss->sio.scan_scanner_type));
127
128 /* now install special handlers */
129 ss->special = &scanjet_special;
130
131 /*
132 * populate the scanio struct with legal values
133 */
134 ss->sio.scan_width = 1200;
135 ss->sio.scan_height = 1200;
136 ss->sio.scan_x_resolution = 100;
137 ss->sio.scan_y_resolution = 100;
138 ss->sio.scan_x_origin = 0;
139 ss->sio.scan_y_origin = 0;
140 ss->sio.scan_brightness = 128;
141 ss->sio.scan_contrast = 128;
142 ss->sio.scan_quality = 100;
143 ss->sio.scan_image_mode = SIM_GRAYSCALE;
144
145 error = scanjet_set_window(ss);
146 if (error) {
147 printf(" set_window failed\n");
148 return;
149 }
150 error = scanjet_compute_sizes(ss);
151 if (error) {
152 printf(" compute_sizes failed\n");
153 return;
154 }
155
156 printf("\n");
157 }
158
159 int
160 scanjet_get_params(ss)
161 struct ss_softc *ss;
162 {
163
164 return (0);
165 }
166
167 /*
168 * check the parameters if the scanjet is capable of fulfilling it
169 * but don't send the command to the scanner in case the user wants
170 * to change parameters by more than one call
171 */
172 int
173 scanjet_set_params(ss, sio)
174 struct ss_softc *ss;
175 struct scan_io *sio;
176 {
177 int error;
178
179 #if 0
180 /*
181 * if the scanner is triggered, then rewind it
182 */
183 if (ss->flags & SSF_TRIGGERED) {
184 error = scanjet_rewind_scanner(ss);
185 if (error)
186 return (error);
187 }
188 #endif
189
190 /* size constraints... */
191 if (sio->scan_width == 0 ||
192 sio->scan_x_origin + sio->scan_width > 10200 || /* 8.5" */
193 sio->scan_height == 0 ||
194 sio->scan_y_origin + sio->scan_height > 16800) /* 14" */
195 return (EINVAL);
196
197 /* resolution (dpi)... */
198 if (sio->scan_x_resolution < 100 ||
199 sio->scan_x_resolution > 400 ||
200 sio->scan_y_resolution < 100 ||
201 sio->scan_y_resolution > 400)
202 return (EINVAL);
203
204 switch (sio->scan_image_mode) {
205 case SIM_BINARY_MONOCHROME:
206 case SIM_DITHERED_MONOCHROME:
207 case SIM_GRAYSCALE:
208 case SIM_COLOR:
209 break;
210 default:
211 return (EINVAL);
212 }
213
214 /* change ss_softc to the new values, but save ro-variables */
215 sio->scan_scanner_type = ss->sio.scan_scanner_type;
216 memcpy(&ss->sio, sio, sizeof(struct scan_io));
217
218 error = scanjet_set_window(ss);
219 if (error) {
220 uprintf("%s: set_window failed\n", ss->sc_dev.dv_xname);
221 return (error);
222 }
223 error = scanjet_compute_sizes(ss);
224 if (error) {
225 uprintf("%s: compute_sizes failed\n", ss->sc_dev.dv_xname);
226 return (error);
227 }
228
229 return (0);
230 }
231
232 /*
233 * trigger the scanner to start a scan operation
234 * this includes sending the mode- and window-data,
235 * and starting the scanner
236 */
237 int
238 scanjet_trigger_scanner(ss)
239 struct ss_softc *ss;
240 {
241 char escape_codes[20];
242 int error;
243
244 error = scanjet_set_window(ss);
245 if (error) {
246 uprintf("%s: set_window failed\n", ss->sc_dev.dv_xname);
247 return (error);
248 }
249 error = scanjet_compute_sizes(ss);
250 if (error) {
251 uprintf("%s: compute_sizes failed\n", ss->sc_dev.dv_xname);
252 return (error);
253 }
254
255 /* send "trigger" operation */
256 strcpy(escape_codes, "\033*f0S");
257 error = scanjet_ctl_write(ss, escape_codes, strlen(escape_codes));
258 if (error) {
259 uprintf("%s: trigger_scanner failed\n", ss->sc_dev.dv_xname);
260 return (error);
261 }
262
263 return (0);
264 }
265
266 int
267 scanjet_read(ss, bp)
268 struct ss_softc *ss;
269 struct buf *bp;
270 {
271 struct scsi_rw_scanner cmd;
272 struct scsipi_xfer *xs;
273 struct scsipi_periph *periph = ss->sc_periph;
274 int error;
275
276 /*
277 * Fill out the scsi command
278 */
279 memset(&cmd, 0, sizeof(cmd));
280 cmd.opcode = READ;
281
282 /*
283 * Handle "fixed-block-mode" tape drives by using the
284 * block count instead of the length.
285 */
286 _lto3b(bp->b_bcount, cmd.len);
287
288 /*
289 * go ask the adapter to do all this for us
290 */
291 xs = scsipi_make_xs(periph,
292 (struct scsipi_generic *) &cmd, sizeof(cmd),
293 (u_char *) bp->b_data, bp->b_bcount,
294 SCANJET_RETRIES, 100000, bp,
295 XS_CTL_NOSLEEP | XS_CTL_ASYNC | XS_CTL_DATA_IN);
296 if (xs == NULL) {
297 /*
298 * out of memory. Keep this buffer in the queue, and
299 * retry later.
300 */
301 callout_reset(&ss->sc_callout, hz / 2, ssrestart,
302 periph);
303 return(0);
304 }
305 #ifdef DIAGNOSTIC
306 if (BUFQ_GET(&ss->buf_queue) != bp)
307 panic("ssstart(): dequeued wrong buf");
308 #else
309 BUFQ_GET(&ss->buf_queue);
310 #endif
311 error = scsipi_command(periph, xs,
312 (struct scsipi_generic *) &cmd, sizeof(cmd),
313 (u_char *) bp->b_data, bp->b_bcount, SCANJET_RETRIES, 100000, bp,
314 XS_CTL_NOSLEEP | XS_CTL_ASYNC | XS_CTL_DATA_IN);
315 /* with a scsipi_xfer preallocated, scsipi_command can't fail */
316 KASSERT(error == 0);
317 ss->sio.scan_window_size -= bp->b_bcount;
318 if (ss->sio.scan_window_size < 0)
319 ss->sio.scan_window_size = 0;
320 return (0);
321 }
322
323
324 /*
325 * Do a synchronous write. Used to send control messages.
326 */
327 int
328 scanjet_ctl_write(ss, buf, size)
329 struct ss_softc *ss;
330 char *buf;
331 u_int size;
332 {
333 struct scsi_rw_scanner cmd;
334 int flags;
335
336 flags = 0;
337 if ((ss->flags & SSF_AUTOCONF) != 0)
338 flags |= XS_CTL_DISCOVERY;
339
340 memset(&cmd, 0, sizeof(cmd));
341 cmd.opcode = WRITE;
342 _lto3b(size, cmd.len);
343 return (scsipi_command(ss->sc_periph, NULL,
344 (struct scsipi_generic *) &cmd,
345 sizeof(cmd), (u_char *) buf, size, 0, 100000, NULL,
346 flags | XS_CTL_DATA_OUT | XS_CTL_DATA_ONSTACK));
347 }
348
349
350 /*
351 * Do a synchronous read. Used to read responses to control messages.
352 */
353 int
354 scanjet_ctl_read(ss, buf, size)
355 struct ss_softc *ss;
356 char *buf;
357 u_int size;
358 {
359 struct scsi_rw_scanner cmd;
360 int flags;
361
362 flags = 0;
363 if ((ss->flags & SSF_AUTOCONF) != 0)
364 flags |= XS_CTL_DISCOVERY;
365
366 memset(&cmd, 0, sizeof(cmd));
367 cmd.opcode = READ;
368 _lto3b(size, cmd.len);
369 return (scsipi_command(ss->sc_periph, NULL,
370 (struct scsipi_generic *) &cmd,
371 sizeof(cmd), (u_char *) buf, size, 0, 100000, NULL,
372 flags | XS_CTL_DATA_IN | XS_CTL_DATA_ONSTACK));
373 }
374
375
376 #ifdef SCANJETDEBUG
377 static void show_es(char *es)
378 {
379 char *p = es;
380 while (*p) {
381 if (*p == '\033')
382 printf("[Esc]");
383 else
384 printf("%c", *p);
385 ++p;
386 }
387 printf("\n");
388 }
389 #endif
390
391 /*
392 * simulate SCSI_SET_WINDOW for ScanJets
393 */
394 int
395 scanjet_set_window(ss)
396 struct ss_softc *ss;
397 {
398 char escape_codes[128], *p;
399
400 p = escape_codes;
401
402 p += sprintf(p, "\033*f%ldP", ss->sio.scan_width / 4);
403 p += sprintf(p, "\033*f%ldQ", ss->sio.scan_height / 4);
404 p += sprintf(p, "\033*f%ldX", ss->sio.scan_x_origin / 4);
405 p += sprintf(p, "\033*f%ldY", ss->sio.scan_y_origin / 4);
406 p += sprintf(p, "\033*a%dR", ss->sio.scan_x_resolution);
407 p += sprintf(p, "\033*a%dS", ss->sio.scan_y_resolution);
408
409 switch (ss->sio.scan_image_mode) {
410 case SIM_BINARY_MONOCHROME:
411 ss->sio.scan_bits_per_pixel = 1;
412 /* use "line art" mode */
413 strcpy(p, "\033*a0T");
414 p += strlen(p);
415 /* make image data be "min-is-white ala PBM */
416 strcpy(p, "\033*a0I");
417 p += strlen(p);
418 break;
419 case SIM_DITHERED_MONOCHROME:
420 ss->sio.scan_bits_per_pixel = 1;
421 /* use dithered mode */
422 strcpy(p, "\033*a3T");
423 p += strlen(p);
424 /* make image data be "min-is-white ala PBM */
425 strcpy(p, "\033*a0I");
426 p += strlen(p);
427 break;
428 case SIM_GRAYSCALE:
429 ss->sio.scan_bits_per_pixel = 8;
430 /* use grayscale mode */
431 strcpy(p, "\033*a4T");
432 p += strlen(p);
433 /* make image data be "min-is-black ala PGM */
434 strcpy(p, "\033*a1I");
435 p += strlen(p);
436 break;
437 case SIM_COLOR:
438 ss->sio.scan_bits_per_pixel = 24;
439 /* use RGB color mode */
440 strcpy(p, "\033*a5T");
441 p += strlen(p);
442 /* make image data be "min-is-black ala PPM */
443 strcpy(p, "\033*a1I");
444 p += strlen(p);
445 /* use pass-through matrix (disable NTSC) */
446 strcpy(p, "\033*u2T");
447 p += strlen(p);
448 break;
449 }
450
451 p += sprintf(p, "\033*a%dG", ss->sio.scan_bits_per_pixel);
452 p += sprintf(p, "\033*a%dL", (int)(ss->sio.scan_brightness) - 128);
453 p += sprintf(p, "\033*a%dK", (int)(ss->sio.scan_contrast) - 128);
454
455 return (scanjet_ctl_write(ss, escape_codes, p - escape_codes));
456 }
457
458 int
459 scanjet_compute_sizes(ss)
460 struct ss_softc *ss;
461 {
462 int error;
463 static const char *wfail = "%s: interrogate write failed\n";
464 static const char *rfail = "%s: interrogate read failed\n";
465 static const char *dfail = "%s: bad data returned\n";
466 char escape_codes[20];
467 char response[20];
468 char *p;
469
470 /*
471 * Deal with the fact that the HP ScanJet IIc uses 1/300" not 1/1200"
472 * as its base unit of measurement. PINT uses 1/1200" (yes I know
473 * ScanJet II's use decipoints as well but 1200 % 720 != 0)
474 */
475 ss->sio.scan_width = (ss->sio.scan_width + 3) & 0xfffffffc;
476 ss->sio.scan_height = (ss->sio.scan_height + 3) & 0xfffffffc;
477
478 switch (ss->sio.scan_image_mode) {
479 case SIM_BINARY_MONOCHROME:
480 case SIM_DITHERED_MONOCHROME:
481 strcpy(escape_codes, "\033*s1025E"); /* bytes wide */
482 break;
483 case SIM_GRAYSCALE:
484 case SIM_COLOR:
485 strcpy(escape_codes, "\033*s1024E"); /* pixels wide */
486 break;
487 }
488 error = scanjet_ctl_write(ss, escape_codes, strlen(escape_codes));
489 if (error) {
490 uprintf(wfail, ss->sc_dev.dv_xname);
491 return (error);
492 }
493 error = scanjet_ctl_read(ss, response, 20);
494 if (error) {
495 uprintf(rfail, ss->sc_dev.dv_xname);
496 return (error);
497 }
498 p = strchr(response, 'd');
499 if (p == 0) {
500 uprintf(dfail, ss->sc_dev.dv_xname);
501 return (EIO);
502 }
503 ss->sio.scan_pixels_per_line = strtoul(p + 1, NULL, 10);
504 if (ss->sio.scan_image_mode < SIM_GRAYSCALE)
505 ss->sio.scan_pixels_per_line *= 8;
506
507 strcpy(escape_codes, "\033*s1026E"); /* pixels high */
508 error = scanjet_ctl_write(ss, escape_codes, strlen(escape_codes));
509 if (error) {
510 uprintf(wfail, ss->sc_dev.dv_xname);
511 return (error);
512 }
513 error = scanjet_ctl_read(ss, response, 20);
514 if (error) {
515 uprintf(rfail, ss->sc_dev.dv_xname);
516 return (error);
517 }
518 p = strchr(response, 'd');
519 if (p == 0) {
520 uprintf(dfail, ss->sc_dev.dv_xname);
521 return (EIO);
522 }
523 ss->sio.scan_lines = strtoul(p + 1, NULL, 10);
524
525 ss->sio.scan_window_size = ss->sio.scan_lines *
526 ((ss->sio.scan_pixels_per_line * ss->sio.scan_bits_per_pixel) / 8);
527
528 return (0);
529 }
Cache object: 8ea520df3d24cbc14d738ae0ba6cdebf
|