ss_scanjet.c revision 1.28.6.3 1 /* $NetBSD: ss_scanjet.c,v 1.28.6.3 2004/09/03 12:45:39 skrll 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.6.3 2004/09/03 12:45:39 skrll 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/ssvar.h>
58
59 #define SCANJET_RETRIES 4
60
61 static int scanjet_get_params(struct ss_softc *);
62 static int scanjet_set_params(struct ss_softc *, struct scan_io *);
63 static int scanjet_trigger_scanner(struct ss_softc *);
64 static int scanjet_read(struct ss_softc *, struct buf *);
65
66 /* only used internally */
67 static int scanjet_ctl_write(struct ss_softc *, char *, u_int);
68 static int scanjet_ctl_read(struct ss_softc *, char *, u_int);
69 static int scanjet_set_window(struct ss_softc *);
70 static int scanjet_compute_sizes(struct ss_softc *);
71
72 /*
73 * structure for the special handlers
74 */
75 static struct ss_special scanjet_special = {
76 scanjet_set_params,
77 scanjet_trigger_scanner,
78 scanjet_get_params,
79 NULL, /* no special minphys */
80 scanjet_read, /* scsi 6-byte read */
81 NULL, /* no "rewind" code (yet?) */
82 NULL, /* no adf support right now */
83 NULL /* no adf support right now */
84 };
85
86 /*
87 * scanjet_attach: attach special functions to ss
88 */
89 void
90 scanjet_attach(struct ss_softc *ss, struct scsipibus_attach_args *sa)
91 {
92 int error;
93
94 SC_DEBUG(ss->sc_periph, SCSIPI_DB1, ("scanjet_attach: start\n"));
95 ss->sio.scan_scanner_type = 0;
96
97 printf("%s: ", ss->sc_dev.dv_xname);
98
99 /* first, check the model (which determines nothing yet) */
100
101 if (!memcmp(sa->sa_inqbuf.product, "C1750A", 6)) {
102 ss->sio.scan_scanner_type = HP_SCANJET_IIC;
103 printf("HP ScanJet IIc");
104 } else if (!memcmp(sa->sa_inqbuf.product, "C2500A", 6)) {
105 ss->sio.scan_scanner_type = HP_SCANJET_IIC;
106 printf("HP ScanJet IIcx");
107 } else if (!memcmp(sa->sa_inqbuf.product, "C2520A", 6)) {
108 ss->sio.scan_scanner_type = HP_SCANJET_IIC;
109 printf("HP ScanJet 4c");
110 } else if (!memcmp(sa->sa_inqbuf.product, "C1130A", 6)) {
111 ss->sio.scan_scanner_type = HP_SCANJET_IIC;
112 printf("HP ScanJet 4p");
113 } else if (!memcmp(sa->sa_inqbuf.product, "C5110A", 6)) {
114 ss->sio.scan_scanner_type = HP_SCANJET_IIC;
115 printf("HP ScanJet 5p");
116 } else {
117 ss->sio.scan_scanner_type = HP_SCANJET_IIC;
118 printf("HP ScanJet (unknown model)");
119 }
120
121 SC_DEBUG(ss->sc_periph, SCSIPI_DB1,
122 ("scanjet_attach: scanner_type = %d\n",
123 ss->sio.scan_scanner_type));
124
125 /* now install special handlers */
126 ss->special = &scanjet_special;
127
128 /*
129 * populate the scanio struct with legal values
130 */
131 ss->sio.scan_width = 1200;
132 ss->sio.scan_height = 1200;
133 ss->sio.scan_x_resolution = 100;
134 ss->sio.scan_y_resolution = 100;
135 ss->sio.scan_x_origin = 0;
136 ss->sio.scan_y_origin = 0;
137 ss->sio.scan_brightness = 128;
138 ss->sio.scan_contrast = 128;
139 ss->sio.scan_quality = 100;
140 ss->sio.scan_image_mode = SIM_GRAYSCALE;
141
142 error = scanjet_set_window(ss);
143 if (error) {
144 printf(" set_window failed\n");
145 return;
146 }
147 error = scanjet_compute_sizes(ss);
148 if (error) {
149 printf(" compute_sizes failed\n");
150 return;
151 }
152
153 printf("\n");
154 }
155
156 static int
157 scanjet_get_params(struct ss_softc *ss)
158 {
159
160 return (0);
161 }
162
163 /*
164 * check the parameters if the scanjet is capable of fulfilling it
165 * but don't send the command to the scanner in case the user wants
166 * to change parameters by more than one call
167 */
168 static int
169 scanjet_set_params(struct ss_softc *ss, struct scan_io *sio)
170 {
171 int error;
172
173 #if 0
174 /*
175 * if the scanner is triggered, then rewind it
176 */
177 if (ss->flags & SSF_TRIGGERED) {
178 error = scanjet_rewind_scanner(ss);
179 if (error)
180 return (error);
181 }
182 #endif
183
184 /* size constraints... */
185 if (sio->scan_width == 0 ||
186 sio->scan_x_origin + sio->scan_width > 10200 || /* 8.5" */
187 sio->scan_height == 0 ||
188 sio->scan_y_origin + sio->scan_height > 16800) /* 14" */
189 return (EINVAL);
190
191 /* resolution (dpi)... */
192 if (sio->scan_x_resolution < 100 ||
193 sio->scan_x_resolution > 400 ||
194 sio->scan_y_resolution < 100 ||
195 sio->scan_y_resolution > 400)
196 return (EINVAL);
197
198 switch (sio->scan_image_mode) {
199 case SIM_BINARY_MONOCHROME:
200 case SIM_DITHERED_MONOCHROME:
201 case SIM_GRAYSCALE:
202 case SIM_COLOR:
203 break;
204 default:
205 return (EINVAL);
206 }
207
208 /* change ss_softc to the new values, but save ro-variables */
209 sio->scan_scanner_type = ss->sio.scan_scanner_type;
210 memcpy(&ss->sio, sio, sizeof(struct scan_io));
211
212 error = scanjet_set_window(ss);
213 if (error) {
214 uprintf("%s: set_window failed\n", ss->sc_dev.dv_xname);
215 return (error);
216 }
217 error = scanjet_compute_sizes(ss);
218 if (error) {
219 uprintf("%s: compute_sizes failed\n", ss->sc_dev.dv_xname);
220 return (error);
221 }
222
223 return (0);
224 }
225
226 /*
227 * trigger the scanner to start a scan operation
228 * this includes sending the mode- and window-data,
229 * and starting the scanner
230 */
231 static int
232 scanjet_trigger_scanner(struct ss_softc *ss)
233 {
234 char escape_codes[20];
235 int error;
236
237 error = scanjet_set_window(ss);
238 if (error) {
239 uprintf("%s: set_window failed\n", ss->sc_dev.dv_xname);
240 return (error);
241 }
242 error = scanjet_compute_sizes(ss);
243 if (error) {
244 uprintf("%s: compute_sizes failed\n", ss->sc_dev.dv_xname);
245 return (error);
246 }
247
248 /* send "trigger" operation */
249 strlcpy(escape_codes, "\033*f0S", sizeof(escape_codes));
250 error = scanjet_ctl_write(ss, escape_codes, strlen(escape_codes));
251 if (error) {
252 uprintf("%s: trigger_scanner failed\n", ss->sc_dev.dv_xname);
253 return (error);
254 }
255
256 return (0);
257 }
258
259 static int
260 scanjet_read(struct ss_softc *ss, struct buf *bp)
261 {
262 struct scsi_rw_scanner cmd;
263 struct scsipi_periph *periph = ss->sc_periph;
264 int error;
265
266 /*
267 * Fill out the scsi command
268 */
269 memset(&cmd, 0, sizeof(cmd));
270 cmd.opcode = READ;
271
272 /*
273 * Handle "fixed-block-mode" tape drives by using the
274 * block count instead of the length.
275 */
276 _lto3b(bp->b_bcount, cmd.len);
277
278 /*
279 * go ask the adapter to do all this for us
280 */
281 error = scsipi_command(periph,
282 (struct scsipi_generic *) &cmd, sizeof(cmd),
283 (u_char *) bp->b_data, bp->b_bcount, SCANJET_RETRIES, 100000, bp,
284 XS_CTL_NOSLEEP | XS_CTL_ASYNC | XS_CTL_DATA_IN);
285 if (error) {
286 printf("%s: not queued, error %d\n", ss->sc_dev.dv_xname,
287 error);
288 if (error == ENOMEM) {
289 /*
290 * out of memory. Keep this buffer in the queue, and
291 * retry later.
292 */
293 callout_reset(&ss->sc_callout, hz / 2, ssrestart,
294 periph);
295 return(0);
296 }
297 } else {
298 ss->sio.scan_window_size -= bp->b_bcount;
299 if (ss->sio.scan_window_size < 0)
300 ss->sio.scan_window_size = 0;
301 }
302 #ifdef DIAGNOSTIC
303 if (BUFQ_GET(&ss->buf_queue) != bp)
304 panic("ssstart(): dequeued wrong buf");
305 #else
306 BUFQ_GET(&ss->buf_queue);
307 #endif
308
309 return (0);
310 }
311
312
313 /*
314 * Do a synchronous write. Used to send control messages.
315 */
316 static int
317 scanjet_ctl_write(struct ss_softc *ss, char *buf, u_int size)
318 {
319 struct scsi_rw_scanner cmd;
320 int flags;
321
322 flags = 0;
323 if ((ss->flags & SSF_AUTOCONF) != 0)
324 flags |= XS_CTL_DISCOVERY;
325
326 memset(&cmd, 0, sizeof(cmd));
327 cmd.opcode = WRITE;
328 _lto3b(size, cmd.len);
329 return (scsipi_command(ss->sc_periph,
330 (struct scsipi_generic *) &cmd,
331 sizeof(cmd), (u_char *) buf, size, 0, 100000, NULL,
332 flags | XS_CTL_DATA_OUT | XS_CTL_DATA_ONSTACK));
333 }
334
335
336 /*
337 * Do a synchronous read. Used to read responses to control messages.
338 */
339 static int
340 scanjet_ctl_read(struct ss_softc *ss, char *buf, u_int size)
341 {
342 struct scsi_rw_scanner cmd;
343 int flags;
344
345 flags = 0;
346 if ((ss->flags & SSF_AUTOCONF) != 0)
347 flags |= XS_CTL_DISCOVERY;
348
349 memset(&cmd, 0, sizeof(cmd));
350 cmd.opcode = READ;
351 _lto3b(size, cmd.len);
352 return (scsipi_command(ss->sc_periph,
353 (struct scsipi_generic *) &cmd,
354 sizeof(cmd), (u_char *) buf, size, 0, 100000, NULL,
355 flags | XS_CTL_DATA_IN | XS_CTL_DATA_ONSTACK));
356 }
357
358
359 #ifdef SCANJETDEBUG
360 static void
361 show_es(char *es)
362 {
363 char *p = es;
364 while (*p) {
365 if (*p == '\033')
366 printf("[Esc]");
367 else
368 printf("%c", *p);
369 ++p;
370 }
371 printf("\n");
372 }
373 #endif
374
375 /*
376 * simulate SCSI_SET_WINDOW for ScanJets
377 */
378 static int
379 scanjet_set_window(struct ss_softc *ss)
380 {
381 char escape_codes[128], *p, *ep;
382
383 p = escape_codes;
384 ep = &escape_codes[128];
385
386 p += snprintf(p, ep - p, "\033*f%ldP", ss->sio.scan_width / 4);
387 p += snprintf(p, ep - p, "\033*f%ldQ", ss->sio.scan_height / 4);
388 p += snprintf(p, ep - p, "\033*f%ldX", ss->sio.scan_x_origin / 4);
389 p += snprintf(p, ep - p, "\033*f%ldY", ss->sio.scan_y_origin / 4);
390 p += snprintf(p, ep - p, "\033*a%dR", ss->sio.scan_x_resolution);
391 p += snprintf(p, ep - p, "\033*a%dS", ss->sio.scan_y_resolution);
392
393 switch (ss->sio.scan_image_mode) {
394 case SIM_BINARY_MONOCHROME:
395 ss->sio.scan_bits_per_pixel = 1;
396 /* use "line art" mode */
397 strlcpy(p, "\033*a0T", ep - p);
398 p += strlen(p);
399 /* make image data be "min-is-white ala PBM */
400 strlcpy(p, "\033*a0I", ep - p);
401 p += strlen(p);
402 break;
403 case SIM_DITHERED_MONOCHROME:
404 ss->sio.scan_bits_per_pixel = 1;
405 /* use dithered mode */
406 strlcpy(p, "\033*a3T", ep - p);
407 p += strlen(p);
408 /* make image data be "min-is-white ala PBM */
409 strlcpy(p, "\033*a0I", ep - p);
410 p += strlen(p);
411 break;
412 case SIM_GRAYSCALE:
413 ss->sio.scan_bits_per_pixel = 8;
414 /* use grayscale mode */
415 strlcpy(p, "\033*a4T", ep - p);
416 p += strlen(p);
417 /* make image data be "min-is-black ala PGM */
418 strlcpy(p, "\033*a1I", ep - p);
419 p += strlen(p);
420 break;
421 case SIM_COLOR:
422 ss->sio.scan_bits_per_pixel = 24;
423 /* use RGB color mode */
424 strlcpy(p, "\033*a5T", ep - p);
425 p += strlen(p);
426 /* make image data be "min-is-black ala PPM */
427 strlcpy(p, "\033*a1I", ep - p);
428 p += strlen(p);
429 /* use pass-through matrix (disable NTSC) */
430 strlcpy(p, "\033*u2T", ep - p);
431 p += strlen(p);
432 break;
433 }
434
435 p += snprintf(p, ep - p, "\033*a%dG", ss->sio.scan_bits_per_pixel);
436 p += snprintf(p, ep - p, "\033*a%dL", (int)(ss->sio.scan_brightness) - 128);
437 p += snprintf(p, ep - p, "\033*a%dK", (int)(ss->sio.scan_contrast) - 128);
438
439 return (scanjet_ctl_write(ss, escape_codes, p - escape_codes));
440 }
441
442 static int
443 scanjet_compute_sizes(struct ss_softc *ss)
444 {
445 int error;
446 static const char *wfail = "%s: interrogate write failed\n";
447 static const char *rfail = "%s: interrogate read failed\n";
448 static const char *dfail = "%s: bad data returned\n";
449 char escape_codes[20];
450 char response[20];
451 char *p;
452
453 /*
454 * Deal with the fact that the HP ScanJet IIc uses 1/300" not 1/1200"
455 * as its base unit of measurement. PINT uses 1/1200" (yes I know
456 * ScanJet II's use decipoints as well but 1200 % 720 != 0)
457 */
458 ss->sio.scan_width = (ss->sio.scan_width + 3) & 0xfffffffc;
459 ss->sio.scan_height = (ss->sio.scan_height + 3) & 0xfffffffc;
460
461 switch (ss->sio.scan_image_mode) {
462 case SIM_BINARY_MONOCHROME:
463 case SIM_DITHERED_MONOCHROME:
464 /* bytes wide */
465 strlcpy(escape_codes, "\033*s1025E", sizeof(escape_codes));
466 break;
467 case SIM_GRAYSCALE:
468 case SIM_COLOR:
469 /* pixels wide */
470 strlcpy(escape_codes, "\033*s1024E", sizeof(escape_codes));
471 break;
472 }
473 error = scanjet_ctl_write(ss, escape_codes, strlen(escape_codes));
474 if (error) {
475 uprintf(wfail, ss->sc_dev.dv_xname);
476 return (error);
477 }
478 error = scanjet_ctl_read(ss, response, 20);
479 if (error) {
480 uprintf(rfail, ss->sc_dev.dv_xname);
481 return (error);
482 }
483 p = strchr(response, 'd');
484 if (p == 0) {
485 uprintf(dfail, ss->sc_dev.dv_xname);
486 return (EIO);
487 }
488 ss->sio.scan_pixels_per_line = strtoul(p + 1, NULL, 10);
489 if (ss->sio.scan_image_mode < SIM_GRAYSCALE)
490 ss->sio.scan_pixels_per_line *= 8;
491
492 /* pixels high */
493 strlcpy(escape_codes, "\033*s1026E", sizeof(escape_codes));
494 error = scanjet_ctl_write(ss, escape_codes, strlen(escape_codes));
495 if (error) {
496 uprintf(wfail, ss->sc_dev.dv_xname);
497 return (error);
498 }
499 error = scanjet_ctl_read(ss, response, 20);
500 if (error) {
501 uprintf(rfail, ss->sc_dev.dv_xname);
502 return (error);
503 }
504 p = strchr(response, 'd');
505 if (p == 0) {
506 uprintf(dfail, ss->sc_dev.dv_xname);
507 return (EIO);
508 }
509 ss->sio.scan_lines = strtoul(p + 1, NULL, 10);
510
511 ss->sio.scan_window_size = ss->sio.scan_lines *
512 ((ss->sio.scan_pixels_per_line * ss->sio.scan_bits_per_pixel) / 8);
513
514 return (0);
515 }
516