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