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