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 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 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 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 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 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 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 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 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 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 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