1 /* $NetBSD: ss_scanjet.c,v 1.50 2009/10/21 21:12:06 rmind Exp $ */
4 * Copyright (c) 1995 Kenneth Stailey. All rights reserved.
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
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.
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.
33 * special functions for the HP ScanJet IIc and IIcx
36 #include <sys/cdefs.h>
37 __KERNEL_RCSID(0, "$NetBSD: ss_scanjet.c,v 1.50 2009/10/21 21:12:06 rmind Exp $");
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>
48 #include <sys/device.h>
49 #include <sys/conf.h> /* for cdevsw */
50 #include <sys/scanio.h>
51 #include <sys/kernel.h>
53 #include <dev/scsipi/scsipi_all.h>
54 #include <dev/scsipi/scsi_all.h>
55 #include <dev/scsipi/scsi_scanner.h>
56 #include <dev/scsipi/scsipiconf.h>
57 #include <dev/scsipi/scsipi_base.h>
58 #include <dev/scsipi/ssvar.h>
60 #define SCANJET_RETRIES 4
62 static int scanjet_get_params(struct ss_softc
*);
63 static int scanjet_set_params(struct ss_softc
*, struct scan_io
*);
64 static int scanjet_trigger_scanner(struct ss_softc
*);
65 static int scanjet_read(struct ss_softc
*, struct buf
*);
67 /* only used internally */
68 static int scanjet_ctl_write(struct ss_softc
*, char *, u_int
);
69 static int scanjet_ctl_read(struct ss_softc
*, char *, u_int
);
70 static int scanjet_set_window(struct ss_softc
*);
71 static int scanjet_compute_sizes(struct ss_softc
*);
74 * structure for the special handlers
76 static struct ss_special scanjet_special
= {
78 scanjet_trigger_scanner
,
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 */
88 * scanjet_attach: attach special functions to ss
91 scanjet_attach(struct ss_softc
*ss
, struct scsipibus_attach_args
*sa
)
95 SC_DEBUG(ss
->sc_periph
, SCSIPI_DB1
, ("scanjet_attach: start\n"));
96 ss
->sio
.scan_scanner_type
= 0;
98 printf("%s: ", device_xname(&ss
->sc_dev
));
100 /* first, check the model (which determines nothing yet) */
102 if (!memcmp(sa
->sa_inqbuf
.product
, "C1750A", 6)) {
103 ss
->sio
.scan_scanner_type
= HP_SCANJET_IIC
;
104 printf("HP ScanJet IIc");
105 } else if (!memcmp(sa
->sa_inqbuf
.product
, "C2500A", 6)) {
106 ss
->sio
.scan_scanner_type
= HP_SCANJET_IIC
;
107 printf("HP ScanJet IIcx");
108 } else if (!memcmp(sa
->sa_inqbuf
.product
, "C2520A", 6)) {
109 ss
->sio
.scan_scanner_type
= HP_SCANJET_IIC
;
110 printf("HP ScanJet 4c");
111 } else if (!memcmp(sa
->sa_inqbuf
.product
, "C1130A", 6)) {
112 ss
->sio
.scan_scanner_type
= HP_SCANJET_IIC
;
113 printf("HP ScanJet 4p");
114 } else if (!memcmp(sa
->sa_inqbuf
.product
, "C5110A", 6)) {
115 ss
->sio
.scan_scanner_type
= HP_SCANJET_IIC
;
116 printf("HP ScanJet 5p");
118 ss
->sio
.scan_scanner_type
= HP_SCANJET_IIC
;
119 printf("HP ScanJet (unknown model)");
122 SC_DEBUG(ss
->sc_periph
, SCSIPI_DB1
,
123 ("scanjet_attach: scanner_type = %d\n",
124 ss
->sio
.scan_scanner_type
));
126 /* now install special handlers */
127 ss
->special
= &scanjet_special
;
130 * populate the scanio struct with legal values
132 ss
->sio
.scan_width
= 1200;
133 ss
->sio
.scan_height
= 1200;
134 ss
->sio
.scan_x_resolution
= 100;
135 ss
->sio
.scan_y_resolution
= 100;
136 ss
->sio
.scan_x_origin
= 0;
137 ss
->sio
.scan_y_origin
= 0;
138 ss
->sio
.scan_brightness
= 128;
139 ss
->sio
.scan_contrast
= 128;
140 ss
->sio
.scan_quality
= 100;
141 ss
->sio
.scan_image_mode
= SIM_GRAYSCALE
;
143 error
= scanjet_set_window(ss
);
145 printf(" set_window failed\n");
148 error
= scanjet_compute_sizes(ss
);
150 printf(" compute_sizes failed\n");
158 scanjet_get_params(struct ss_softc
*ss
)
165 * check the parameters if the scanjet is capable of fulfilling it
166 * but don't send the command to the scanner in case the user wants
167 * to change parameters by more than one call
170 scanjet_set_params(struct ss_softc
*ss
, struct scan_io
*sio
)
176 * if the scanner is triggered, then rewind it
178 if (ss
->flags
& SSF_TRIGGERED
) {
179 error
= scanjet_rewind_scanner(ss
);
185 /* size constraints... */
186 if (sio
->scan_width
== 0 ||
187 sio
->scan_x_origin
+ sio
->scan_width
> 10200 || /* 8.5" */
188 sio
->scan_height
== 0 ||
189 sio
->scan_y_origin
+ sio
->scan_height
> 16800) /* 14" */
192 /* resolution (dpi)... */
193 if (sio
->scan_x_resolution
< 100 ||
194 sio
->scan_x_resolution
> 400 ||
195 sio
->scan_y_resolution
< 100 ||
196 sio
->scan_y_resolution
> 400)
199 switch (sio
->scan_image_mode
) {
200 case SIM_BINARY_MONOCHROME
:
201 case SIM_DITHERED_MONOCHROME
:
209 /* change ss_softc to the new values, but save ro-variables */
210 sio
->scan_scanner_type
= ss
->sio
.scan_scanner_type
;
211 memcpy(&ss
->sio
, sio
, sizeof(struct scan_io
));
213 error
= scanjet_set_window(ss
);
215 uprintf("%s: set_window failed\n", device_xname(&ss
->sc_dev
));
218 error
= scanjet_compute_sizes(ss
);
220 uprintf("%s: compute_sizes failed\n", device_xname(&ss
->sc_dev
));
228 * trigger the scanner to start a scan operation
229 * this includes sending the mode- and window-data,
230 * and starting the scanner
233 scanjet_trigger_scanner(struct ss_softc
*ss
)
235 char escape_codes
[20];
238 error
= scanjet_set_window(ss
);
240 uprintf("%s: set_window failed\n", device_xname(&ss
->sc_dev
));
243 error
= scanjet_compute_sizes(ss
);
245 uprintf("%s: compute_sizes failed\n", device_xname(&ss
->sc_dev
));
249 /* send "trigger" operation */
250 strlcpy(escape_codes
, "\033*f0S", sizeof(escape_codes
));
251 error
= scanjet_ctl_write(ss
, escape_codes
, strlen(escape_codes
));
253 uprintf("%s: trigger_scanner failed\n", device_xname(&ss
->sc_dev
));
261 scanjet_read(struct ss_softc
*ss
, struct buf
*bp
)
263 struct scsi_rw_scanner cmd
;
264 struct scsipi_xfer
*xs
;
265 struct scsipi_periph
*periph
= ss
->sc_periph
;
269 * Fill out the scsi command
271 memset(&cmd
, 0, sizeof(cmd
));
275 * Handle "fixed-block-mode" tape drives by using the
276 * block count instead of the length.
278 _lto3b(bp
->b_bcount
, cmd
.len
);
281 * go ask the adapter to do all this for us
283 xs
= scsipi_make_xs(periph
,
284 (struct scsipi_generic
*) &cmd
, sizeof(cmd
),
285 (u_char
*) bp
->b_data
, bp
->b_bcount
,
286 SCANJET_RETRIES
, 100000, bp
,
287 XS_CTL_NOSLEEP
| XS_CTL_ASYNC
| XS_CTL_DATA_IN
);
290 * out of memory. Keep this buffer in the queue, and
293 callout_reset(&ss
->sc_callout
, hz
/ 2, ssrestart
,
298 if (bufq_get(ss
->buf_queue
) != bp
)
299 panic("ssstart(): dequeued wrong buf");
301 bufq_get(ss
->buf_queue
);
303 error
= scsipi_execute_xs(xs
);
304 /* with a scsipi_xfer preallocated, scsipi_command can't fail */
306 ss
->sio
.scan_window_size
-= bp
->b_bcount
;
308 if (ss
->sio
.scan_window_size
< 0)
309 ss
->sio
.scan_window_size
= 0;
316 * Do a synchronous write. Used to send control messages.
319 scanjet_ctl_write(struct ss_softc
*ss
, char *tbuf
, u_int size
)
321 struct scsi_rw_scanner cmd
;
325 if ((ss
->flags
& SSF_AUTOCONF
) != 0)
326 flags
|= XS_CTL_DISCOVERY
;
328 memset(&cmd
, 0, sizeof(cmd
));
330 _lto3b(size
, cmd
.len
);
332 return (scsipi_command(ss
->sc_periph
,
333 (void *)&cmd
, sizeof(cmd
), (void *)tbuf
, size
, 0, 100000, NULL
,
334 flags
| XS_CTL_DATA_OUT
));
339 * Do a synchronous read. Used to read responses to control messages.
342 scanjet_ctl_read(struct ss_softc
*ss
, char *tbuf
, u_int size
)
344 struct scsi_rw_scanner cmd
;
348 if ((ss
->flags
& SSF_AUTOCONF
) != 0)
349 flags
|= XS_CTL_DISCOVERY
;
351 memset(&cmd
, 0, sizeof(cmd
));
353 _lto3b(size
, cmd
.len
);
355 return (scsipi_command(ss
->sc_periph
,
356 (void *)&cmd
, sizeof(cmd
), (void *)tbuf
, size
, 0, 100000, NULL
,
357 flags
| XS_CTL_DATA_IN
));
378 * simulate SCSI_SET_WINDOW for ScanJets
381 scanjet_set_window(struct ss_softc
*ss
)
383 char escape_codes
[128], *p
, *ep
;
386 ep
= &escape_codes
[128];
388 p
+= snprintf(p
, ep
- p
, "\033*f%ldP", ss
->sio
.scan_width
/ 4);
389 p
+= snprintf(p
, ep
- p
, "\033*f%ldQ", ss
->sio
.scan_height
/ 4);
390 p
+= snprintf(p
, ep
- p
, "\033*f%ldX", ss
->sio
.scan_x_origin
/ 4);
391 p
+= snprintf(p
, ep
- p
, "\033*f%ldY", ss
->sio
.scan_y_origin
/ 4);
392 p
+= snprintf(p
, ep
- p
, "\033*a%dR", ss
->sio
.scan_x_resolution
);
393 p
+= snprintf(p
, ep
- p
, "\033*a%dS", ss
->sio
.scan_y_resolution
);
395 switch (ss
->sio
.scan_image_mode
) {
396 case SIM_BINARY_MONOCHROME
:
397 ss
->sio
.scan_bits_per_pixel
= 1;
398 /* use "line art" mode */
399 strlcpy(p
, "\033*a0T", ep
- p
);
401 /* make image data be "min-is-white ala PBM */
402 strlcpy(p
, "\033*a0I", ep
- p
);
405 case SIM_DITHERED_MONOCHROME
:
406 ss
->sio
.scan_bits_per_pixel
= 1;
407 /* use dithered mode */
408 strlcpy(p
, "\033*a3T", ep
- p
);
410 /* make image data be "min-is-white ala PBM */
411 strlcpy(p
, "\033*a0I", ep
- p
);
415 ss
->sio
.scan_bits_per_pixel
= 8;
416 /* use grayscale mode */
417 strlcpy(p
, "\033*a4T", ep
- p
);
419 /* make image data be "min-is-black ala PGM */
420 strlcpy(p
, "\033*a1I", ep
- p
);
424 ss
->sio
.scan_bits_per_pixel
= 24;
425 /* use RGB color mode */
426 strlcpy(p
, "\033*a5T", ep
- p
);
428 /* make image data be "min-is-black ala PPM */
429 strlcpy(p
, "\033*a1I", ep
- p
);
431 /* use pass-through matrix (disable NTSC) */
432 strlcpy(p
, "\033*u2T", ep
- p
);
437 p
+= snprintf(p
, ep
- p
, "\033*a%dG", ss
->sio
.scan_bits_per_pixel
);
438 p
+= snprintf(p
, ep
- p
, "\033*a%dL", (int)(ss
->sio
.scan_brightness
) - 128);
439 p
+= snprintf(p
, ep
- p
, "\033*a%dK", (int)(ss
->sio
.scan_contrast
) - 128);
441 return (scanjet_ctl_write(ss
, escape_codes
, p
- escape_codes
));
445 scanjet_compute_sizes(struct ss_softc
*ss
)
448 static const char *wfail
= "%s: interrogate write failed\n";
449 static const char *rfail
= "%s: interrogate read failed\n";
450 static const char *dfail
= "%s: bad data returned\n";
451 char escape_codes
[20];
456 * Deal with the fact that the HP ScanJet IIc uses 1/300" not 1/1200"
457 * as its base unit of measurement. PINT uses 1/1200" (yes I know
458 * ScanJet II's use decipoints as well but 1200 % 720 != 0)
460 ss
->sio
.scan_width
= (ss
->sio
.scan_width
+ 3) & 0xfffffffc;
461 ss
->sio
.scan_height
= (ss
->sio
.scan_height
+ 3) & 0xfffffffc;
463 switch (ss
->sio
.scan_image_mode
) {
464 case SIM_BINARY_MONOCHROME
:
465 case SIM_DITHERED_MONOCHROME
:
467 strlcpy(escape_codes
, "\033*s1025E", sizeof(escape_codes
));
472 strlcpy(escape_codes
, "\033*s1024E", sizeof(escape_codes
));
475 error
= scanjet_ctl_write(ss
, escape_codes
, strlen(escape_codes
));
477 uprintf(wfail
, device_xname(&ss
->sc_dev
));
480 error
= scanjet_ctl_read(ss
, response
, 20);
482 uprintf(rfail
, device_xname(&ss
->sc_dev
));
485 p
= strchr(response
, 'd');
487 uprintf(dfail
, device_xname(&ss
->sc_dev
));
490 ss
->sio
.scan_pixels_per_line
= strtoul(p
+ 1, NULL
, 10);
491 if (ss
->sio
.scan_image_mode
< SIM_GRAYSCALE
)
492 ss
->sio
.scan_pixels_per_line
*= 8;
495 strlcpy(escape_codes
, "\033*s1026E", sizeof(escape_codes
));
496 error
= scanjet_ctl_write(ss
, escape_codes
, strlen(escape_codes
));
498 uprintf(wfail
, device_xname(&ss
->sc_dev
));
501 error
= scanjet_ctl_read(ss
, response
, 20);
503 uprintf(rfail
, device_xname(&ss
->sc_dev
));
506 p
= strchr(response
, 'd');
508 uprintf(dfail
, device_xname(&ss
->sc_dev
));
511 ss
->sio
.scan_lines
= strtoul(p
+ 1, NULL
, 10);
513 ss
->sio
.scan_window_size
= ss
->sio
.scan_lines
*
514 ((ss
->sio
.scan_pixels_per_line
* ss
->sio
.scan_bits_per_pixel
) / 8);